6 ms·
Stuffed-Na(a)N: stuff your NaNs
- axblount 1y agoThis is usually called NaN-boxing and is often used to implement dynamic languages. https://piotrduperas.com/posts/nan-boxing https://piotrduperas.com/posts/nan-boxing
- addoo 1y agoI appreciate this article a lot more because it contains an iota of benchmarking with an explanation about why this might be more performant. Especially since my first thought was 'wouldn't this require more instructions to be executed?' The original post seems really weird to me. I would have dismissed it as someone's hobby project, but... that doesn't seem like what it's trying to be.
- wging 1y agoIt's a joke project.
- Tuna-Fish 1y ago"More instructions to execute" is not synonymous with "slower". NaNboxing lets you use less memory on certain use cases. Because memory access is slow and caches are fixed size, this is usually a performance win, even if you have to do a few extra ops on every access.
- thehappypm 1y agoI mean, a modern computer is operating the gigahertz range. Adding a few extra bitwise instructions might be something like a nanosecond. Which is absolutely fleeting compared to memory operations.
- jasonthorsness 1y agoI wonder if IEEE-754 designers anticipated this use case during development. Great article - this kind of "outside-the-normal-use-case" requires a very careful reading of the specifications/guarantees of the language.
- purplesyringa 1y agoIEEE-754 seems to say that NaN payloads are designed to contain "retrospective diagnostic information inherited from invalid or unavailable data and results". While NaN boxing in particular probably wasn't the intention, untouched payloads in general absolutely were.
- userbinator 1y agoIt's especially useful since everything is 64 bits uniformly.
- pwdisswordfishz 1y agoLike JavaScript. Which should immediately raise the question how it could possibly work on engines that employ it themselves. Turns out... it won't.
- tetris11 1y agoAre there any security implications of NaN-Boxing? If I encode data into the exponent of these NaN values, propagate them around the internet, and decode them elsewhere.... is that a security risk? Or this just falls into the category of "weird encryption"
- rcxdude 1y agoDefine security risk. Obviously this data could come from untrusted sources. Another consideration is that this data may not serialize correctly: most text serialization protocols will not distinguish different NaN values. It's possible for there to be some data confusion in your pipeline as well, if you don't control all the code that touches those bytes.
- ufo 1y agoI'd be surprised. It's surprisingly difficult to setialize NaN values. Can't do it on JSON, for example.
- yencabulator 1y agoLots of formats just put the IEEE-754 bytes on the wire, as-is. For example, Protocol Buffers: https://protobuf.dev/programming-guides/encoding/ https://protobuf.dev/programming-guides/encoding/
- dunham 1y agoI learned about this when trying to decode data from Firefox IndexedDB. (I was extracting Tana data.) Their structured clone data format uses nan-boxing for serialization.
- AaronAPU 1y agoReminds me of using the highest bit(s) of 64-bit ints to stuff auxiliary data into lockfree algorithms. So long as you’re aware of your OS environment you can enable some efficiencies you couldn’t otherwise.
- eru 1y agoThat's why OCaml has 63 bit integers by default: the language itself uses the last bit to help with GC.
- amelius 1y agoI'm curious why we have not-a-number, but not not-a-string, not-a-boolean, not-an-enum, and not-a-mycustomtype.
- ok_computer 1y agoBecause representing infinity is not possible outside of symbolic logic and isn’t encodable in floats. I think it is a simple numerical reason and not a deeper computer reason.
- Sharlin 1y agoWell, infinity is totally representable with IEEE 754 floats. For example 1.0/0.0 == +inf, -1.0/0.0 == -inf, but 0.0/0.0 == NaN.
- amelius 1y agoA smart compiler should be able to figure out a better value for 0/0, depending on context. For example: for i in range(0, 10): print(i/0.0) In this case it should probably print +inf when i == 0. But: for i in range(-10, 10): print(i/0.0) Now it is not clear, but at least we know it's an infinity so perhaps we need a special value +-inf. And: for i in range(-10, 10): print(i/i) In this case, the value for 0/0 can be 1.
- Sharlin 1y agoWell, it could, but that would be against the spec. The hardware implements IEEE 754, most languages guarantee IEEE 754, and transforming code so that 0.0/0.0 doesn't result in NaN would be invalid.
- ok_computer 1y agoI'm incorrect. there's a spec discussed here https://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html https://www.gnu.org/software/libc/manual/html_node/Infinity-...
- dzaima 1y agoThis doesn't work on Firefox, as it normalizes NaNs as they're extracted from ArrayBuffers. Presumably because SpiderMonkey uses NaN-boxing itself, and thus just doesn't have any way to represent actual non-canonical NaN floats.
- moffkalast 1y agoDammit Mozilla, first no WebGPU, now this?! /s
- sjrd 1y agoThe spec mandates normalization of NaNs in ArrayBuffers. If other engines do not normalize, I believe it's a bug in those engines!
- dzaima 1y agoChromium & Node (i.e. V8) do seem to just not normalize: arr = new Float64Array([0]); arr[0] = new Float64Array(new BigUint64Array([-123n]).buffer)[0]; [...new Uint8Array(arr.buffer)] // [133, 255, 255, 255, 255, 255, 255, 255]
- vhcr 1y agoRunning the following snippets: f = new Float64Array(1) dv = new DataView(f.buffer) dv.setBigInt64(0, -123n) console.log([...new Uint8Array(f.buffer)]) f = new Float64Array(1) new BigUint64Array(f.buffer)[0] = -123n console.log([...new Uint8Array(f.buffer)]) f = new Float64Array(1) f[0] = new Float64Array(new BigUint64Array([-123n]).buffer)[0] console.log([...new Uint8Array(f.buffer)]) f = new Float64Array([NaN]) console.log([...new Uint8Array(f.buffer)]) I get on Chrome: [255, 255, 255, 255, 255, 255, 255, 133] [133, 255, 255, 255, 255, 255, 255, 255] [133, 255, 255, 255, 255, 255, 255, 255] [0, 0, 0, 0, 0, 0, 248, 127] And on Firefox: [ 255, 255, 255, 255, 255, 255, 255, 133 ] [ 133, 255, 255, 255, 255, 255, 255, 255 ] [ 0, 0, 0, 0, 0, 0, 248, 127 ] [ 0, 0, 0, 0, 0, 0, 248, 127 ] It looks like both implementations are buggy, Since both the DataView and buffer versions should be normalized. https://bugzilla.mozilla.org/show_bug.cgi?id=1393085 https://bugzilla.mozilla.org/show_bug.cgi?id=1393085
- haxiomic 1y agoThat's curious! Does anyone know why the spec was designed to allow so many possible NaN bit patterns? Seems like just one would do!
- dzaima 1y agoAs long as you want all bit patterns to be Some float, there's not really one bit pattern you can chop out of somewhere (or, three, rather - both infinities, and NaN). Taking, say, the 3 smallest subnormal numbers, or the three largest numbers, or whatever, would be extremely bad for allowing optimizations/algorithms/correctness checkers to reason about operations in the abstract.
- brewmarche 1y agoYou can put diagnostic information about the cause of the NaN in the bits. IEEE754 doesn’t mandate any format though, leaving it up to the implementation. You can check section 6 of IEEE754:2019 (which also talks about the possibility to use it to extend the standard to wider ranges, more infinities or other stuff)
- vitaut 1y agoI made a garlic nan: https://www.godbolt.org/z/enjv1c7Tf https://www.godbolt.org/z/enjv1c7Tf
- dgroshev 1y agoI love it, how did I not think about this!
- scorchingjello 1y agoSince the first time I had Indian food sometime in 1987 I have always called naan “not a number bread” and no one has ever laughed. I feel like i may have found my people.
- dspillett 1y agoThis may appeal: https://doubleyoudoubledoubleewe.www.dash.deeohhtee.dash-dash.enneeetee.net/tmp/isnan.png https://doubleyoudoubledoubleewe.www.dash.deeohhtee.dash-das...
- carterschonwald 1y agoThe nan bits were intended to help provide more informative float error diagnostics at the language implementation level for non crashing code. I wonder if I’ll ever get to exploring it myself.
- johnklos 1y agoIt's a shame this won't work on VAX.
- o11c 1y agoNote that there are a lot of exciting new float-related functions in C23, many related to NaN handling, signed zeros, or precision in edge cases. Some have been in glibc for years; others not yet. The API for `getpayload`/`setpayload`/`setpayloadsig` looks a little funny but it's easy enough to wrap (just consider the edge cases; in particular remember that whether 0 is valid for quiet or signaling NaNs is platform-dependent). Finally we have a reliable `roundeven`, and the convenience of directly calling `nextup`/`nextdown` (but note that you'll still only visit one of the zeros). The new `fmaximum` family is confusing, but I think I have it clear: without 'imum' (legacy): prefer non-NaN with 'imum': zeros distinguished (-0 < +0) with '_num': prefer a non-NaN, also signal if applicable with '_mag': compare as if fabs first `totalorder` has been often-desired; the other new comparisons not so much. `rootn` and `compoundn` are surprisingly tricky to implement yourself. I still have one testcase I'm not sure why I have to hand-patch, and I'm not sure which implementation choice keeps the best precision (to say nothing of correct rounding mode).
- Etheryte 1y ago> but note that you'll still only visit one of the zeros Out of curiosity, isn't this what you'd pretty much universally want? Or maybe I misunderstand the intent here?
- o11c 1y agoI find `nextup/`nextdown` useful for when you want to calculate `{f(nextdown(x)), f(x), f(nextup(x))}` and see if some property holds (or to what degree it holds) for both `x` and its surrounding values. It's annoying that one such surrounding value gets skipped. That said, quite often you need to handle signed zero specially already, and if you don't the property you're testing might sufficiently differ in the interesting way for the smallest-magnitude subnormals.
- clhodapp 1y agoI guess I just don't get it because the before & after cases don't seem to be showing remotely comparable use cases. In the "before" use-cases, the programmer has accidentally written a bug and gets back a single NaN from a logical operation they performed as a result. In the "after" cases, the programmer already has some data and explicitly decides to convert it to a NaN encoding. But instead of actually getting back a NaN that is secretly carrying their data, they actually get a whole array of NaN's, which duck type differently, and thus are likely to disappear into another NaN or an undefined if they are propagated through an API. Like.. I get that the whole thing is supposed to be humorously absurd but... It just doesn't land unless there's something technical I'm missing to connect up the pieces.
- brap 1y agoIt sounds like you might be interested in the Enterprise Edition
- roywiggins 1y agoFour NaNs? Four? That's insane. https://m.youtube.com/watch?v=feJlRDLX0iQ https://m.youtube.com/watch?v=feJlRDLX0iQ
- yapyap 1y ago‘Stuff your nan’ is this intentional? From a UK perspective this sounds wild
- phpnode 1y agoStuffed Na(a)N as in naan bread https://commons.m.wikimedia.org/wiki/File:Stuffed_Naan.JPG https://commons.m.wikimedia.org/wiki/File:Stuffed_Naan.JPG
- gitroom 1y agowell i totally relate to calling naan not-a-number bread, it's been stuck in my head ever since i learned about NaNs tbh