Every day I learn something new... and stupid.

4 years ago
Hey kids, did you know that JavaScript doesn't have integers?

That's right! All JavaScript numbers (either primitive numbers, or the Number object, and don't even get me started on how stupid it is that there is even a distinction between the two) are actually double floats!

Now, in any Lisp-derived language, you don't expect to have immediate-integers with the full bit-width of the native word size, since a few bits need to be consumed by runtime tagging. If the language implementor is really clever, you can get away with losing only 2 bits, but mostly people are less clever than that, and you lose 5-8 bits, so it's reasonable to expect that MAXINT on a modern computer is at least 2^56, 2^59 being more reasonable.

(It would also be reasonable to assume that any sane language runtime would have integers transparently degrade to BIGNUMs, making the choice of accuracy over speed, but of course that almost never happens, because the painful transition from 32-bit architectures to 64-bit architectures apparently taught the current crop of CS graduates no lesson better than, "Oh, did I say that 32 bits should be enough for everybody? I meant 64 bits." But again I digress.)

Anyway, the happy-fun-time result of the fact that JavaScript dodged the MAXINT problem by making all numbers be floats means that while there is not technically a MAXINT, you can't actually do meaningful integer arithmetic on anything bigger than 2^53! Not because 11 bits are being used for tags, but because the underlying IEEE-754 floats can't unambiguously represent integers larger than that!

It's like an AI Koan: "One day a student came to Moon and said, 'I understand how to avoid using BIGNUMs! We will simply use floats!' Moon struck the student with a stick. The student was enlightened."

Even better is that this behavior is explicitly laid out in the language specification: ECMA 262, 8.5:

The finite nonzero values are of two kinds: 2^64-2^54 of them are normalised, [...] The remaining 2^53-2 values are denormalised [...] Note that all the positive and negative integers whose magnitude is no greater than 2^53 are representable in the Number type.

This means that if you write a JavaScript implementation that does not faithfully reproduce the bug that arithmetic on integers greater than 2^53 silently does something stupid, then your implementation of the language is non-conforming.

Oh, also bitwise logical operations only have defined results (per the spec) up to 32 bits. And the result for out-of-range inputs is not an error condition, but silently mod 2^32.

I swear, it's a wonder anything works at all.

Previously, previously, previously, previously.

Tags: computers, doomed, firstperson, lisp, retrocomputing

86 Responses:

  1. spacer ahruman says:
    4 years ago at 3:00 pm

    Spidermonkey traditionally does use two bits for tagging of integers, but with a 30-bit payload regardless of architecture. (Last time I checked, they were working on switching to a 128-bit value type.) Numbers are converted between 30-bit integer and pointer-to-double representation as needed. Not that this is helpful in any way.

    ECMAScript Harmony will probably add a separate type system for "value types", which is specifically motivated by IBM's desire to support decimal floats and Mozilla's desire not to. The details haven't worked out, but the general idea is a category of types that have operator overloading but no properties. It should be possible for a host app to implement sane integers on top of this. So as long as you don't need to do anything before 2013 and will control your host app, you're home free!

    • spacer jwz says:
      4 years ago at 3:09 pm

      Wow, I was unaware that anyone more recent than, like, Charles Babbage thought that decimal floats were a good idea...

      Operator overloading. I'd have to file that under "now you have two problems"...

      • spacer ahruman says:
        4 years ago at 3:55 pm

        I'm kinda on the fence about operator overloading. It leads to much stupidity, but on the other hand, doing maths on non-primitive types (vector algebra and so forth) without it is painful. Separating extensible-things-with-operators from objects might actually be a good idea. I kinda do this already with Objective-C++, but using ObjC++ gives me at least five problems anyway. :-)

        (Obviously I'll defer to Brendan on all the on-topic stuff.)

      • spacer jackwilliambell says:
        4 years ago at 3:56 pm

        Actually decimal floats have a very specific use case: computations on monetary values.

        Regular floats may do some funky things in a complex computation (especially one involving lots of inputs, like an average). This is fine so long as you have all your decimal places, but falls apart when you are restricted to two or so decimal places (like money). To compound the problem, two different CPUs might come up with different answers for the same algorithm and inputs.

        With decimal floats (or BCD) you avoid the whole issue by doing the calculations the way an old fashioned hand-crank calculator would.

        Given IBM's customer base, this is a pretty important consideration for them.

        • spacer BrendanEich says:
          4 years ago at 4:18 pm

          https://bugzilla.mozilla.org/show_bug.cgi?id=5856 is the most-dup'ed JS bug, last I checked.

          Try javascript:alert(.1+.2) and consider people doing non-monetary arithmetic, say for CSS style computation.

          It's a problem, and IBM is right to want a solution, but IEEE754r is a glorious, decade-long, multi-format (last I heard; to placate two competing hardware vendors) mess. The standards body was reduced to using Robert's Rules in committee to prevent bloodshed at one time.

          Some on Ecma TC39 (JavaScript) are warm to bignums, but the whole decimal jam-job has made everyone a bit gun-shy.

          /be

          • spacer jered says:
            4 years ago at 11:51 pm

            I did some time in standards (SNIA, in this case), and AFAICT the biggest hobby-horse in IBM is fixed-point arithmetic. We chose the absolute minimal set of useful primitive types for our data abstraction (unlike JavaScript, apparently) and at the last minute the IBM folks came back with a fixed-point data requirement.

            Also, everything in our standard had to be a superset of all of their products, but that wasn't a problem unique to IBM.

        • spacer flipzagging says:
          4 years ago at 4:22 pm

          When it comes to financial software, every decent developer already uses integer millis (1000x the smallest currency unit).

          • spacer BrendanEich says:
            4 years ago at 4:30 pm

            or vampire-squid banks:

            $ bc
            bc 1.06
            Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
            This is free software with ABSOLUTELY NO WARRANTY.
            For details type `warranty'.
            2^53
            9007199254740992
            ./1000
            9007199254740
            ./365.25
            24660367569
            ./24
            1027515315
            ./3600
            285420

            The last three division steps show how JS's Date object fits time since the epoch in a double and still cover a very large extrapolated-Gregorian calendar.

            /be

        • spacer jp_larocque says:
          4 years ago at 7:41 am

          Or you could use rational numbers, which are more general, more elegant, and possibly even more efficient. And hey, Lisp does that by default, too.

          • spacer BrendanEich says:
            4 years ago at 9:38 am

            Sam Tobin-Hochstadt, private correspondence:

            "Bignums are great. There's no reason for the performance problems to stop people from switching to bignums in any non-C language, and it's fairly straightforward to optimize to use fixnum operations in many cases. I think [trace-based JITting] will do well here, also. Exact rationals are a whole other can of worms. I admit to liking having them around in Scheme, but I rarely use them.
            ...
            I think [fast enough and better semantics than double] is the case for bignums, but not for rationals. I don't know of any other awesome solutions for rationals, though. Fast, precise, rational - pick any two."

            /be

      • spacer jason_watkins says:
        4 years ago at 10:16 am

        Decimal floats are very useful for avoiding rounding/representation errors when data moves back and forth between pen and paper and computerized systems. Some businesses have financial processes where this is essential.

      • spacer withoutthedrama says:
        4 years ago at 2:58 pm

        Actually, William Kahan said "A major decrease in avoidable silly errors can be achieved by letting most (not all) scientists, engineers, ... and other computer users perform most (not all) of their floating-point arithmetic in decimal; therefore that is the way we must go."

    • spacer BrendanEich says:
      4 years ago at 3:35 pm

      SpiderMonkey used one bit for ages, not two. Now we use NaN boxing.

      It was not Mozilla who stopped IBM's mad IEEE754r decimal jam attempt. We actually had a prototype patch from Sam Ruby. All the other browser vendors, plus Doug Crockford of Yahoo!, were solidly opposed. In many ways Mozilla was IBM's best friend on this point, and we are still paying for it.

      Value types are a dark horse, but if based on proxies they may turn out well.

      The bignum strawman could just be a new primitive type, and happen quickly. IBM would not be happy, but many others would. Right now web sites are doing crypto in JS using ints (bitwise ops) and double multiplies!

      /be

  2. spacer inoah says:
    4 years ago at 3:03 pm

    Is this Brendan's fault, or did this creep in sometime later?

    • spacer jwz says:
      4 years ago at 3:06 pm

      No idea, but it sounds like the sort of 4am shortcut of which that first implementation was entirely composed, so I'd guess it's been in there from the start...

      • spacer BrendanEich says:
        4 years ago at 3:26 pm

        Yes, it was there from the start. But bignums were not in the cards. JS had to "look like Java" only less so, be Java's dumb kid brother or boy-hostage sidekick. Plus, I had to be done in ten days or something worse than JS would have happened.

        So double by default, int under the hood, and bitwise ops are 32-bit int (uint if you use >>>). I blame Java.

        /be

        • spacer duskwuff says:
          4 years ago at 4:03 pm

          So, since I have to ask: What would we be stuck with if JS hadn't happened?

          • spacer BrendanEich says:
            4 years ago at 4:08 pm

            Something like PHP only worse, which Netscape's VP Eng killed fast (in early July 1995, if memory serves; I did JS in early-mid-May) since it was "third" after Java and JS. Two new web programming languages were hard enough to justify...

            /be

            • spacer dasht says:
              4 years ago at 4:59 pm

              I think JS was mostly a great thing to come along, in its way so please take this in the context of that spirit. I respect a lot of what you've done -- but also have a gripe that relates to this thread.

              As respectfully as I can ... I'm sorry that this will be harsh but, this bit, where you say:

              "Yes, it was there from the start. But bignums were not in the cards. JS had to "look like Java" only less so, be Java's dumb kid brother or boy-hostage sidekick. Plus, I had to be done in ten days or something worse than JS would have happened."

              WTF IS THE MATTER WITH YOU!?!?!

              By which I mean... we all know the creation story of Jscript but in that story and in your reiteration of it here... basically you bent over for your masters. You had the option of a few of you standing up, not bending over... and quite possibly crashing the damn company (then running to the press and working on setting up your own separate thing). You might have lost, in other words.. You might collectively have been tossed out on your butt, blacklisted, replaced, and have had no impact .... but I don't think it was terribly likely. With a collective spine you could have won. Better quality AND well earned riches. Instead, hackers sold out. I think you could have made that "F.U." threat and leveraged it for more say about "Doing Things Right" in the engineering department. Instead, y'all wussed out, rushed, and took lots of money for it. At least that's how it looked from nearby.

              From where I sat, back then (just down the road)... the silly valley execs and money people were talking about you hackerish types there behind your back all up and down the valley. They played you guys. They were mostly bluffing. They knew all along you were their highest cards.... they just wanted to be able to brag about how they got your labor on the cheap and in a rush. And, no, they didn't really appreciate what you were trying to do. They heaped a bunch of needless stress on y'all with the effect of reduced quality essentially so they could justify their existence to their friends.

              • spacer jwz says:
                4 years ago at 5:13 pm

                Brendan's house and my nightclub thank us for selling out early and often.

                • spacer dasht says:
                  4 years ago at 5:43 pm

                  And, I do too. I tried to convey that but just to be really clear... I want both. I want the world where you win big and the world where you don't blame it all on Teh Man but rather sometimes stand up to him and win for the team, not just your accounts. You are both, so far as I can tell, smart, hard working, socially beneficial people on balance and perhaps it is my brain bug or perhaps I'm right but my perception is you screwed up in your relation to big money back then, around stuff like the topic. Younger hackers reading this should wonder if maybe they can do better. My impression from what I saw around the corridors of power back then is that you guys folded like a house of cards compared to what you could have gotten away with. (Maybe that's why they paid you the big bucks. :-)

              • spacer flipzagging says:
                4 years ago at 6:25 pm

                In my experience, when it comes to the very best engineers, what you're paying for is not that they will do everything perfectly. It's their ability to know what to neglect, in favor of getting the important work done.

              • spacer mister_borogove says:
                4 years ago at 7:18 pm

                Cue Tool's "Hooker With A Penis".

                • spacer Joel Webber says:
                  4 years ago at 3:55 pm

                  +1. This song should be automatically played each time someone busting ass gets accused of selling out by an armchair quarterback!

              • spacer BrendanEich says:
                4 years ago at 7:37 pm

                It's a good question how much we sold out, and although jwz was earlier than I (dumbass me had an offer in spring '94 to join Netscape on the first floor, but I stayed loyal to MicroLunacy^H^H^H^H^H^HUnity), I am pretty sure we didn't trade technical merit for money.

                I think you have us wrong. We didn't sell out -- others (later-comers, who did far less work) made much more than we did. We were naive, in point of fact. If we had it all to do over again, I think we would (in hindsight and with your advice) use our leverage to get better technical and financial outcomes.

                But at the time, mostly we felt the need to move very quickly, not to make money but because we knew Microsoft was coming after us. Microsoft low-balled Netscape in late '94, and the Jims (Clark and Barksdale) told them to pound sand. After that, we felt the monster truck riding up on our little Yugo's rear bumper, month by month.

                If you appreciate this and it is accurate, consider that JavaScript (please, not "JScript") saved you from VBScript.

                As far as us not selling out: live and learn. I'm not sure I'll ever have the opportunity to apply this knowledge, but here it is for you kids who may end up in a rocket-like startup.

                Either get your unfair share, or get your technical goods, or both (in your dreams) -- but don't just work hard to try to invent something new in a hurry, to improve the nascent Web. You might not have the good grace I've had with Mozilla and Firefox to make up for it.

                /be

            • spacer jwz says:
              4 years ago at 6:19 pm

              I'm still bummed that I failed to talk you in to making #!/usr/bin/javascript work back then, because I think that we were still in the window where we had a shot at smothering Perl in the crib...

              • spacer emeb says:
                4 years ago at 7:43 pm

                Now there's something to dream about.

              • spacer claimid.com/pixel says:
                4 years ago at 9:35 am

                I'm still holding out hope for /usr/bin/javascript :)

                • spacer Sami Samhuri says:
                  4 years ago at 10:53 am

                  It's here.

                  #!/usr/bin/env node

                  console.log('hello JS world!')

                  • spacer cananian says:
                    4 years ago at 3:55 pm

                    gjs-console also works like this, and Rhino's shell mode will also do in a pinch.

                    And yes, I've written shell scripts in JavaScript -- under time pressure, no less. With gjs-console giving you access to a decent standard library (everything in GNOME, more or less), it's not bad at all.

        • spacer Brian Mastenbrook says:
          4 years ago at 5:30 pm

          I don't begrudge you the lack of bignums; the state of bignum libraries back then was not terrific. What I don't get is the lack of proper tail calling. Once you understand it, it's not any harder than doing it wrong. How did that get missed?

          • spacer BrendanEich says:
            4 years ago at 7:26 pm

            Ten days to implement the lexer, parser, bytecode emitter (which I folded into the parser; required some code buffering to reorder things like the for(;;) loop head parts and body), interpreter, built-in classes, and decompiler. I had help only for jsdate.c, from Ken Smith of Netscape (who, per our over-optimistic agreement, cloned java.util.Date -- Y2K bugs and all! Gosling...).

            Sorry, not enough time for me to analyze tail position (using an attribute grammar approach: