-3

I found the following strange behavior. Adding some floats result in "random" accuracy.

so first I run go version go1.12 darwin/amd64 on macOS Mojave (10.14.3) with Intel i7 2,6 Ghz

the behavior occur in the following Example:

func TestFloatingAddition(t *testing.T) {
    f1 := float64(5)
    f2 := float64(12.1)
    f5 := float64(-12.1)
    f3 := f1 + f2 // 17.1
    f4 := f3 + f5 // 5.000000000000002
    if f4 != f1 {
        t.Fatal("addition is not reversable")
    }
}

Can someone explain to me why f4 takes on this strange value, and what can I do to fix it?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189

2 Answers2

0

This is not a problem with Go-lang (or C, C++, Java, Python, Ruby), or any modern language which uses IEEE-754 floating point to represent floating point numbers. There are numbers that are not exactly representable using binary or decimal based floating point storage formats.

Floating point numbers are represented using IEEE-754 floating point as three parts, a sign bit, mantissa, and exponent (using exponent bias, offset by 127, 1023, etc, subtracted from exponent). The mantissa is encoded as a binary sequence, and essentially left/right shifted exponent bits to form binary fractions. And with binary fractions lies the problem.

In the same way that the fraction 1/3 is 'relatively prime' in base-10, and cannot be exactly represented in decimal, certain numbers cannot be expressed exactly using binary fractions. Decimal numbers are 'relatively prime' to binary numbers, since 10=2*5 has the factor 5. You cannot express 1/5 exactly as binary fraction, just as the fractions 1/3, 1/7, 1/11, 1/13, 1/17, etc (notice the pattern of prime numbers here?) cannot be expressed exactly in either decimal or binary fractions. The internal representation will always approximate these numbers, and some string conversion libraries use conversions to reduce the approximation error.

What can you do? If you are using only linear arithmetic operators, you could use fixed-point decimal libraries (that is what [shudder] Cobol does).

Some libraries store fractional numbers as ratios of two whole numbers, but this does not solve the problem when you introduce functions such as square root which can produce irrational numbers.

ChuckCottrill
  • 4,360
  • 2
  • 24
  • 42
-2

A Possible solution could be:

const float64EqThreshold = 1e-9

func equalEnought(a,b float64) bool {
    return math.Abs(a - b) <= float64EqThreshold
}

so the function would look like

func TestFloatingAddition(t *testing.T) {
    f1 := float64(5)
    f2 := float64(12.1)
    f5 := float64(-12.1)
    f3 := f1 + f2 // 17.1
    f4 := f3 + f5 // 5.000000000000002
    if equalEnought(f1, f4){
        t.Fatal("addition is not reversable")
    }
}