Giter Site home page Giter Site logo

brazzy / floating-point-gui.de Goto Github PK

View Code? Open in Web Editor NEW
1.0K 1.0K 165.0 321 KB

Website that provides concise answers to common questions about floating-point numbers.

Home Page: http://floating-point-gui.de/

Ruby 0.53% HTML 87.56% Java 9.28% CSS 1.88% PHP 0.74%

floating-point-gui.de's People

Contributors

atefbb avatar brazzy avatar briandiggs avatar chrismear avatar dependabot[bot] avatar dominikwilkowski avatar edgar-bonet avatar f3ndot avatar gaearon avatar gregs1104 avatar igorsantos07 avatar imjasonmiller avatar javimbk avatar jjconti avatar mobsean avatar nd9600 avatar opengrid avatar phoneixs avatar pkral avatar rtheunissen avatar sahapasci avatar telet avatar theghosthucodes avatar tilsammans avatar tweimer avatar valeriyvan avatar veleek avatar whatthefrog avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

floating-point-gui.de's Issues

String comparisons

I love this guide -- I'm going to send it to the rest of my development team. One thing I think is missing is some information on comparing floats by converting them to strings. This is often the easiest way to test for equality, when you're only concerned with how the numbers will be displayed. You can just format both numbers to the number of decimal places that you'll be displaying (e.g. using sprintf), and compare the strings.

language based.

All of this is very language based. For C, C++ and other languages which use the native hardware floating point support you are correct. There are languages however which don't follow these rules. Scheme for example is quite capable of exact infinite precision arithmetic.

using integers is not bad

  • It's more work… bugs… in regard to rounding
    Not really. For one, it may be more work to use an external library, secondly: with money you just don't round. Ever. You present a rounded result when you have to and carry whatever rounding error there was at that point over. Don't lose money to rounding no matter if it's gain or loss.
  • Integers have complete precision, but very limited range…
    A signed bigint storing microcents can hold more than 92 billion dollars. Whatever business you do, that should be sufficient for a long time.
  • The implicit decimal point is hard to change…
    And why would you ever do that? Place the decimal point so it can hold eight digits after the whole unit. That's enough for centuries of accurately calculated compound interest or for paying individual bytes of a 2¢/MiB rate and it doesn't care whether it holds dollars, yens, euros or whatever.
  • The number of subunits in currencies change with time…
    Why would that ever matter? If a dollar has no longer 100 but 10 or 10000 cents, half a dollar would still be half a dollar. What changes is the desired output formatting, not the stored data precision.

A point to be made in favour of integers is performance. Using libraries providing for arbitrarily accurate decimal objects is horrendously slow, especially in a setting where you have to shovel a live stream of many individual events into a billing system. It may not matter that such objects use far more memory than just the 64bits of a bigint, but it matters a whole lot whether putting a price tag onto an individual event takes 120 or 9800 cpu cycles, both in hardware needed and power cost.

Using integers for monetary amounts is fine for most applications. Don't round, instead format output only when you need to present payable amounts and store the cut for later. You may need something else if you are a very large bank or insurance company, but for any small to medium scale business you'll be perfectly okay. There are commercial rating/billing solutions for mobile telco companies out there storing microcents for dozens of brands with millions of customers, and they've never produced a single call event or invoice that was not priced accurately.

Disparity between Smallest/Largest floats and Wikipedia page

On this page, the minimum and maximum values for floats are listed as being on the order of 10e-324 and 10e308, respectively, but the Wikipedia page (listed right above), cites the minimum being on the order of 10e-1074. I assume this is because until 10e-324 you have full digit precision, but I feel like this should be explained or at least mentioned.

For PHP, real unit test scenario - can be added to docs ;)

Unit test file

<?php

declare(strict_types=1);

namespace Tests\Unit;

use App\Traits\FloatComparison;
use PHPUnit\Framework\TestCase;

class FloatComparisonTest extends TestCase
{
    use FloatComparison;

    public function testV1(): void
    {
        $toCheck = (float) '1714.23';
        $items = [ // sum of that is 1714.23
            73.03,
            59.74,
            90.03,
            67.5,
            33.07,
            87.83,
            73.03,
            47.15,
            66.12,
            67.5,
            49.56,
            92.24,
            47.65,
            12.14,
            50.77,
            80.84,
            49.56,
            45.64,
            73.03,
            38.36,
            97.21,
            11.96,
            37.86,
            87.83,
            5.02,
            91.97,
            90.03,
            8.41,
            33.07,
            46.08,
        ];
        $amount = 0;

        foreach ($items as $item) {
            $amount += $item;
        }

        self::assertTrue(self::nearlyEqual($toCheck, $amount));
    }
}

Assertion file:

<?php

declare(strict_types=1);

namespace App\Traits;

trait FloatComparison
{
    /**
     * @link https://floating-point-gui.de/errors/comparison/
     *
     * @param float $a // 1714.23
     * @param float $b // 1714.23
     *
     * @param float $epsilon // 2.2204460492503E-16
     * @param float $floatMin // 2.2250738585072e-308
     * @param floatMax // 1.7976931348623157E+308
     *
     * @return bool
     */
    public static function nearlyEqual(float $a, float $b): bool
    {
        $absA = (float) abs($a); // 1714.23
        $absB = (float) abs($b); // 1714.23
        $diff = (float) abs($a - $b); // 2.2737367544323E-13

        if ($a == $b) { // shortcut, handles infinities
            return true;
        }

        if ($a == 0 || $b == 0 || ($absA + $absB < PHP_FLOAT_MIN)) {
            // a or b is zero or both are extremely close to it
            // relative error is less meaningful here
            return $diff < (PHP_FLOAT_EPSILON * PHP_FLOAT_MIN);
        }

        // use relative error
        return $diff / min(($absA + $absB), PHP_FLOAT_MAX) < PHP_FLOAT_MIN;
    }
}

Addition and subtraction

* Addition and subtraction are dangerous, because when numbers of different magnitudes are involved,

I don't think this line really represents what is happening. The typical problem is trying to subtract 2 numbers that are almost equal. An example is computing (exp(eps) - 1) / eps. For a small eps, exp(eps) - 1 may be computed with insufficient accuracy and the final result is typically inaccurate. Adding/subtracting numbers that have different magnitudes typically does not lead to issues in a computation.
See "Catastrophic cancellation" in your reference
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Round to even is ambiguously defined.

The description in rounding.html of the "Round to Even" method is confusingly worded. The "it" referenced in "...increase the digit only if it's not even" is ambiguous. I would rewrite that sentence, after looking up the method in Wikipedia, as "If it is equal to half the base, pick the direction that yields an even number."

Write/add decimal/binary converter

Site needs a JavaScript-based converter between decimal and binary. Needs to support fractions, of course. Ideally, it should be aware of recurring digits, show these overlined and also allow this notation in the input. Other bases would be a bonus.

Potentialy erroneous test case?

Hi!

I'm currently porting your nearlyEqual-implementation and test cases to C++. Hoping to send you the results in a day or two.

However, one of the test cases fails and I'm wondering if it might in fact be an erroneous test case?

/** Comparisons of numbers on opposite sides of 0 */
    @Test
    public void opposite() {
        assertFalse(nearlyEqual(1.000000001f, -1.0f));
        assertFalse(nearlyEqual(-1.0f, 1.000000001f));
        assertFalse(nearlyEqual(-1.000000001f, 1.0f));
        assertFalse(nearlyEqual(1.0f, -1.000000001f));
        assertTrue(nearlyEqual(10 * Float.MIN_VALUE, 10 * -Float.MIN_VALUE)); //<-- inverted assert? 
        assertFalse(nearlyEqual(10000 * Float.MIN_VALUE, 10000 * -Float.MIN_VALUE));
    }

The second to last test; nearlyEqual(10 * MIN_VALUE, 10 * -MIN_VALUE) , evaluates to false for me. Is there something weird going on in my end, or should that test actually be assertFalse?

My Google Test case for reference:

TEST(NearlyEqual, opposite) {
    static constexpr auto MIN = std::numeric_limits<float>::min();
    EXPECT_FALSE(nearly_equal(1.000000001f, -1.0f));
    EXPECT_FALSE(nearly_equal(-1.0f, 1.000000001f));
    EXPECT_FALSE(nearly_equal(-1.000000001f, 1.0f));
    EXPECT_FALSE(nearly_equal(1.0f, -1.000000001f));
    EXPECT_TRUE(nearly_equal(10.0f * MIN, 10.0f * -MIN)); //this fails for me    
    EXPECT_FALSE(nearly_equal(10000.0f * MIN, 10000.0f * -MIN));
}

I am using the same epsilon (0.00001f) as you, and I'm not compiling with the fast floating point flag.

Discussion: Would chosing integers with an implicit fixed point really be so bad?

I stumbled over your arguments against using integer with implicit fixed point and would like to contribute an alternative view point for your consideration:

1 Overflowing Integers

This one might have been a strong counter-argument in the era of 16- or maybe even 32-bit systems. But we have moved on and with 64-bit machines being the default, it is rarely an issue for many applications. Just for visualization, consider how large 2^64 mm would be
And then there still is long

2 Changing Numbers of Subunits

IMHO this is not an inherent problem of integers as a data type but faulty requirements engineering by the programmer. Either one knows his scope beforehand or falls back to the smallest reasonably possible unit. (Of course you could construct cases like trying to express 1946 [Hungarian Pengö](Hungarian pengő) in US Dollars, even if it made not much sense anymore )

3 Rounding Modes

This is also not a problem related to integers per-se. One always has to chose the correct rounding mode depending on application.

4 Implicit Decimal Point

For most higher-level languages this is not a problem at all. There are a sufficient amount of constructs to keep the programmer save.
Usually, the larger or more precise numbers are affixed to some kind of unit which can be encapsulated by Objects which keep track of the whole implicit decimal point affair themselves. If in doubt (or lazy) one can always use one of the existing fixed-point libraries.

Cheers,
fer-rum

Rational numbers for money

With big numbers, £10/3 + £10/3 + £10/3 === £9.99..., because £3.33... + £3.33... + £3.33... === £9.99....
With rational numbers, £10/3 + £10/3 + £10/3 === £10.
I'm using BigRational.js.

a bug in nearlyEqual?

Hi @brazzy ,

When checking out the nearlyEqual function, I got buzzled with the following codelet:

        final float diff = Math.abs(a - b);
        ...
	} else if (a == 0 || b == 0 || diff < Float.MIN_NORMAL) {
		// a or b is zero or both are extremely close to it
		// relative error is less meaningful here
		return diff < (epsilon * Float.MIN_NORMAL);

that failed in comparing 0.3 and 0.30000000000000004 for me (I am testing the codelet in Python).

Namely, the condition diff < Float.MIN_NORMAL does not mean that a and b are "extremely close to zero", as is stated in the comment.

Take a and b that are close to eachother but both are far from zero. With such input, the above takes wrong code branch, IMHO. Or am I missing something obvious here?

I suggest the following fix:

          } else if (a == 0 || b == 0 || (diff < Float.MIN_NORMAL && absA < Float.MIN_NORMAL)) {

Best regards,
Pearu

NeralyEqual function is inefficient

The code to test if floats are nearly equal is not very efficient. It is a problem when the test is to be performed in speed critical application like graphic.

Could there be a more efficient solution ?

Please fix explanation of "0.0/0.0"

On the http://floating-point-gui.de/errors/comparison/ page you say:
"When both a and be are zero. 0.0/0.0 is “not a number”, which returns false for all comparisons."

This is not entirely true, and a very dangerous assumption. Many languages such as C will cause the program to abort with a divide by zero error. Therefore, please change this reference to properly reflect this.

Thanks for the guide.

nearlyEqual function incorrect for values on opposite side of 0

When we have for example the values -0.000001 and 0.000001, nearlyEqual always returns false for an epsilon < 0.
This is because on the last line "diff / Math.min((absA + absB), Float.MAX_VALUE)" will always return 1.
Some extra care should be taken when a and b are on opposite sides of 0.
I'll try to work something out, but I think that in this case an absolute comparison of diff against epsilon is valid.

Large denominators are not a problem

The page Rounding Errors contains this paragraph:

Large Denominators In any base, the larger the denominator of an (irreducible) fraction, the more digits it needs in positional notation. A sufficiently large denominator will require rounding, no matter what the base or number of available digits is. For example, 1/1000 cannot be accurately represented in less than 3 decimal digits, nor can any multiple of it (that does not allow simplifying the fraction).

This is incorrect. A large denominator does not necessarily imply rounding. For example, the number 1/1000 can be exactly represented with only one decimal digit in the significand:

1/1000 = 1 × 10−3

The paragraph is of course correct if interpreted in the context of fixed point numbers. However, the whole site is about floating point. The page on floating point numbers, which in the menu comes before this one, already introduced the concept using decimal floating point (the same scientific notation I am using here) as a teaching aid. The paragraph right before this one links back to floating point numbers and frames the subject of the page as being about the rounding errors with floating point numbers. At this particular point in the material, it makes no sense to discuss rounding issues specific to fixed point.

I have no simple alternative to suggest. My understanding is that this first bullet point is meant to cover the cases not covered by the following ones. Namely those numbers that can can be exactly represented with a finite number of significant digits in the chosen base, but require more digits than available in the format. A large denominator is not a sufficient condition. Neither is a large numerator. A sufficient condition would be for both the numerator and the denominator to be large, but I am not sure how to word that in a way that remains simple to understand.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.