Live data from Hacker News

0.1 and 0.2 Returns 0.30000000000000004 (2018)

qntm.org

101–110 of 161 posts

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#101
post #94

One of the many reasons I think we all would've been better off, had Brendan Eich decided he'd been able to simply use Scheme within the crazy time constraint he'd been given, rather than create JavaScript, :) is that Scheme comes with a distinction between exact and inexact numbers, in its numerical tower: https://en.wikipedia.org/wiki/Numerical_tower One change I'd consider making to Scheme, and to most high-level…

This would make particular sense in a language like python, which no one (should?) be using for systems programming.

Agreed. Though, for reasons, I had to write essentially a userland device driver in Python (complete with buffer management and keyboard decoder). It was rock-solid in production, in remote appliances, and I was very glad Python was up to that. :)

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#103

Indeed, and therefore: 0.1 + 0.2 != 0.3 You can check it in the JavaScript console. This actually makes me wonder if anyone's ever attempted a floating-point representation that builds in an error range , and correctly propagated/amplified error over operations. E.g. a simple operation like "1 / 10" (to generate 0.1) would be stored not as a single floating-point value, but really as the range between the closest rep…

> Then floating point arithmetic could be used to actually reliably test equality without ever generating false negatives.

The flip side is that you generate plenty of false positives once your error ranges get large enough. This happens pretty readily if you e.g. perform iterations that are supposed to keep the numbers at roughly the same scale.

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#104
post #74

Earlier quoted context omitted.

> Please, tell me, mister, how would you perform complex numerical calculations efficiently? If your calculation turned out to be incorrect it doesn't matter if it's efficient. Correct FP calculation requires error analysis, which is a concrete definition of "how to use it". If you mostly use packaged routines like LAPACK, then you don't exactly need FP; you need routines that internally use FP.

> Correct FP calculation requires error analysis No, it does not. Please, don't make it seem harder than it needs to. 99% applications, if you don't do anything stupid you are completely fine. If you care for precision so much the last digit make difference for you you are probably one of very few cases. I remember somebody giving an example circumference of solar system showing uncertainty of the value of Pi availab…

> if you don't do anything stupid you are completely fine

In this case, "doing anything stupid" includes equality checks. This is the sort of footgun that a huge number of people are going to run into.

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#105
post #94

One of the many reasons I think we all would've been better off, had Brendan Eich decided he'd been able to simply use Scheme within the crazy time constraint he'd been given, rather than create JavaScript, :) is that Scheme comes with a distinction between exact and inexact numbers, in its numerical tower: https://en.wikipedia.org/wiki/Numerical_tower One change I'd consider making to Scheme, and to most high-level…

A Scheme system that implemented exact reals as unnormalized floating decimal (IEEE 754-2008), coupled with a directive that said `numbers with a decimal point should/should not be read as exact' would be wonderful, not just for financial things, but also for teaching students.

It's actually easy to implement that slight variation in Racket, as a `#lang` or reader extension.

As an example of a Scheme-ish `#lang`, here's a Racket `#lang sicp` that I made to mimic MIT Scheme, as well as add a few things needed for SICP: https://github.com/sicp-lang/sicp/blob/master/sicp/main.rkt

It would be even easier to make a `#lang better-scheme`, by defining just a few changes relative to `racket-base`, such as how numbers are read.

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#106
> does not interpret the 0.1 as the real number

The focus should be on _rational_ numbers. This particular example is all about representation error - precision is implicated, but not the cause.

Ignore precision for a second: The inputs 0.1 and 0.2 are intended to be _rational_. This means they can be accurately represented finitely (unlike an irrational number like PI). Now when using fractions they can _always_ be accurately represented finitely in any base:

  1/10=
  base 10: 1/10
  base  2: 1/1010

  2/10=
  base 10:  2/10
  base  2: 10/1010
The neat thing about rationals, is that when using the four basic arithmetic operations: two rational inputs will always produce one rational output :) this is relevant: 1/10 and 2/10 are both rationals, there is no fundamental reason that addition cannot produce 3/10. When using a format that has no representation error (i.e fractions) the output will be rational for all rational inputs (given enough precision, which is not a realistic issue in this case). When we add these particular numbers in our heads however, almost everyone uses decimals (base 10 floating point), and in this particular case that doesn't cause a problem, but what about 1/3?

This is the key: rationals cannot always be represented finitely in floating point formats, but this is merely an artifact of the format and the base. Different bases have different capabilities:

  1/10=
  base 10: 0.1
  base  2: 0.00011001100110011r

  2/10=
  base 10: 0.2
  base  2: 0.00110011001100110r

  1/3=
  base 10: 0.33333333333333333r
  base  2: 0.01010101010101010r
IEEE754 format is a bit more complicated than above, but this is sufficient to make the point.

If you can grok that key point (representation error), here's the real understanding of this problem:

Deception 1: The parser has to convert '0.1' decimal into base 2, which will cause the periodic significand '1001100110011' (not accurately stored at any precision)... yet when you ask for it back, the formater magically converts it to '0.1' why? because the parser and formater have symmetrical error :) This is kinda deceptive, because it makes it look like storage is accurate if you don't know what's going on under the hood.

Deception 2: Many combinations of arithmetic on simple rational decimal inputs also have rational outputs from the formatter, which furthers the illusion. For example, nether 0.1 or 0.3 are representable in base 2, yet 0.1 + 0.3 will be formatted to '0.4' why? It just happens that the arithmetic on those inaccurate representations added up to the same error that the parser produces when parsing '0.4', and since the parser and formatter produce symmetric error, the output is a rational decimal.

Deception 3: Most of us grew up with calculators, or even software calculator programs. All of these usually round display values to 10 significant decimals by default, which is quite a bit less than the max decimal output of a double. This always conceals any small representation errors output by the formatter after arithmetic on rational decimal inputs - which makes calculators look infallible when doing simple math.

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#108
post #12
post #2

Floating point considered harmful Edit: this is not a blanket statement. It was meant in the context.

"People repeating stuff without understanding it considered harmful." Floating point is extremely useful. Too bad so many people have no idea how and when to use it. Including some people that design programming languages. Please, tell me, mister, how would you perform complex numerical calculations efficiently? I guess we should just forget about drones and bunch other stuff because 90% of developers have no clue ho…

What makes you think that everyone around is concerned with efficiency? With FP as the only syntactically sound number system in a language, you basically lose a == operator, as well as an ability to check if your job was finished. Or you use ints and carry fixed points around, which is viable only in few types of languages with operator overloading, like c++. Edit: even when using FP you have to carry initial precisions around, like in:

  var n = get_n() // valid to .5g
  n = transform(n)
  ...
   // 5 carried over here
You can’t even infer the precision from a FP number alone, especially if it is close to log10(53). /edit

In a proper-numbers lang, if someone needed FP numbers, they could just 0.1f. Otherwise 0.1 would mean just that, and counting by 0.1+rand(100) from 1000000 to 0 would not make you scratch your head at the end of the loop and worry whether the rest is just a FP error or an algorithmic error which must be fixed.

90% of developers who know how to use FP still hate it in non-FP tasks, because there is no 0.1nobs literal, how about that.

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#109

In R, there are functions for practical equality (to within a tolerance that makes sense on the local machine), e.g. > all.equal(0.1+0.2,0.3) [1] TRUE and functions for actual equality, e.g. > identical(0.1+0.2,0.3) [1] FALSE

See also https://stackoverflow.com/a/253874

Re: 0.1 and 0.2 Returns 0.30000000000000004 (2018)

#110
If anyone would like to know more then read this paper from 1991 by David Goldberg What every computer scientist should know about floating-point arithmetic, very accessible content even if you are not from CS field.

http://pages.cs.wisc.edu/~david/courses/cs552/S12/handouts/g...

Post reply on HN