Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

That's not unique to JS.

  $ irb
  irb(main):001:0> 0.1 + 0.2
  => 0.30000000000000004
  irb(main):002:0>


  $ iex
  Erlang/OTP 22 [erts-10.6.2] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [hipe]

  Interactive Elixir (1.10.2) - press Ctrl+C to exit (type h() ENTER for help)
  iex(1)> 0.1 + 0.2
  0.30000000000000004
  iex(2)>

  $ python
  Python 2.7.17 (default, Dec  2 2019, 13:23:33)
  [GCC 4.2.1 Compatible Apple LLVM 11.0.0 (clang-1100.0.33.12)] on darwin
  Type "help", "copyright", "credits" or "license" for more information.
  >>> 0.1 + 0.2
  0.30000000000000004
  >>>


I know, it was just a tongue-in-cheek comment but oh well


Interestingly, though, some languages get it right:

    northrup@Topaz:~/Desktop$ perl -de1
    
    Loading DB routines from perl5db.pl version 1.55
    Editor support available.
    
    Enter h or 'h h' for help, or 'man perldebug' for more help.
    
    main::(-e:1): 1
      DB<1> say 0.1 + 0.2
    0.3
[…]

    northrup@Topaz:~$ csi
    CHICKEN
    (c) 2008-2019, The CHICKEN Team
    (c) 2000-2007, Felix L. Winkelmann
    Version 5.1.0 (rev 8e62f718)
    linux-unix-gnu-x86-64 [ 64bit dload ptables ]
    
    #;1> (+ 0.1 0.2)
    0.3
[…]

    northrup@Topaz:~$ sbcl
    This is SBCL 1.5.8, an implementation of ANSI Common Lisp.
    More information about SBCL is available at <http://www.sbcl.org/>.
    
    SBCL is free software, provided as is, with absolutely no warranty.
    It is mostly in the public domain; some portions are provided under
    BSD-style licenses.  See the CREDITS and COPYING files in the
    distribution for more information.
    * (+ 0.1 0.2)
    0.3


Unless they're using an alternate exact representation for floats by default, that's just rounding them when printing.


That's a fair point about the Perl example, but Scheme and Common Lisp (including both Chicken and SBCL, last I checked) support (if not outright require) full numeric towers with rational number types, so these are indeed exact representations (unless you explicitly request binary floats for e.g. performance reasons) and not just rounding during printing.

There are lots of other examples of doing it "right" (using more suitable numeric types) over at https://0.30000000000000004.com/ (alongside a bunch of examples that, to your point, just round, or to the GP's point, just stick to binary floats).


Yeah, in CL the 0.1 is read syntax for a float (IEEE-754ish on most modern implementations.) If you want exactly 0.1, you’d have to say 1/10. The printer is probably cheating and rounding to the output you expect (I think Python 3 may do this now?)

Aside from financial applications, there’s very little reason to care about the trailing remainder.


> The printer is probably cheating and rounding to the output you expect

    northrup@Topaz:~$ sbcl
    This is SBCL 1.5.8, an implementation of ANSI Common Lisp.
    More information about SBCL is available at <http://www.sbcl.org/>.
    
    SBCL is free software, provided as is, with absolutely no warranty.
    It is mostly in the public domain; some portions are provided under
    BSD-style licenses.  See the CREDITS and COPYING files in the
    distribution for more information.
    * (= (+ 0.1 0.2) 0.3)
    T
    * (= (- (+ 0.1 0.2) 0.3) 0)
    T
> Aside from financial applications

"Financial applications" happen to be pretty common reasons for number crunching :)


This just means that 0.3 has the same internal representation as the result of (0.1 + 0.2).

Internally, they're probably both 0.30000000000000004 (depending on precision), so an equality check returns true.

It could also be that they're both 3/10 rational numbers, but given other tests in this thread that's likely not the case out the box.


    northrup@Topaz:~$ sbcl
    This is SBCL 1.5.8, an implementation of ANSI Common Lisp.
    More information about SBCL is available at <http://www.sbcl.org/>.
    
    SBCL is free software, provided as is, with absolutely no warranty.
    It is mostly in the public domain; some portions are provided under
    BSD-style licenses.  See the CREDITS and COPYING files in the
    distribution for more information.
    * (- 0.3 3/10)
    0.0
    * (= (- 0.3 3/10) 0)
    T
    * (= (- 3/10 0.3) 0)
    T
    * (= 0.3 3/10)
    NIL
So yeah, 0.3 and 3/10 are definitely distinct, but still apparently net out to exactly 0 nonetheless.


    CL-USER(3): (rational (+ 0.1 0.2))
    
    5033165/16777216
    CL-USER(3): (rational 0.3)
    
    5033165/16777216


I am aware of numeric towers and actually checked whether that was going on.

sbcl 1.4.16 uses single floats: https://ideone.com/ruw5qi

I don't know enough about Scheme to dig under the covers to see how it's being represented internally.


Scheme is the same. If you type a number with a dot in it, you'll get an "inexact" number, which is floating point. If you prefix it with #e, you'll get an exact representation of the entered number (so, e.g. #e0.1 gives you 1/10)

The output of inexact numbers is typically truncated. In CHICKEN, you can use flonum-print-precision to tweak that. In an example, straight from the manual:

    > (flonum-print-precision 17)
    > 0.1
    0.10000000000000001


sbcl (and other Common Lisps) uses what you want it to use: read-default-float-format

(setf read-default-float-format 'single-float) (+ 0.1 0.2) => 0.3

(setf read-default-float-format 'double-float) (+ 0.1 0.2) => 0.30000000000000004

On LispWorks 7.0 I get 0.30000000000000005 for double-float. Hmm.


Per my other comment, SBCL's defaults still cause (= (- (+ 0.1 0.2) 0.3) 0) → T. I guess they're technically floats (or at the very least not rationals) given that (= 0.3 3/10) → NIL.


It depends. Do you want deterministic compute time? Then you have to settle for something inaccurate.


I have burnt myself once with perl: numbers that are printed the same are not necessarily equal.

  DB<1> say((0.1+0.2) != 0.3)
  1


that is floating point for you, not Perl


I don't think it's so much a matter of getting it right as much as it is about preferring the performance gain and ease of use of hardware-accelerated constant-space floating-point numbers. At least, that was probably the case when programming in something like C/C++. I don't imagine there's much point to it in ruby or python for example, other than that floating-point is standard by now.


Comparing lisps with their full numeric towers to run of the mill procedural languages is a little unfair ;)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: