14

What's the difference between double.infinity and double.maxFinite in Dart?

In Flutter, it looks like both do the same thing. When should I use each of them in Flutter?

goodonion
  • 1,401
  • 13
  • 25
  • 4
    They represent conceptually different things. `double.infinity` represents, well, infinity. `double.maxFinite` represents the largest value that a `double` can store that is *not* `infinity`. – jamesdlin May 10 '20 at 21:11

3 Answers3

4

From a strictly data-oriented view, the difference is that double.maxFinite represents the maximum-allowed value that the double data type can contain, whereas double.infinity represents, well, infinity. Since Dart strictly defines double as a 64-bit floating point number on all platforms, the value of double.maxFinite is therefore known to be 1.7976931348623157e+308. (The specific reason why is fairly technical, but jpopesculian's answer goes into that somewhat, and generally speaking, those details aren't usually necessary to understand for simple app development anyway.)

In Flutter, most of the time, there isn't a huge practical difference between the two values. The place where you will see these constants is usually in terms of sizing widgets, and generally when you want a widget to be as big as possible, you pass double.infinity to its width and/or height:

SizedBox(
  width: 500,
  height: 500,
  child: Container(
    width: double.infinity,
    height: double.infinity,
  ),
)

In this example, the child Container is given an infinite width and height. Flutter will calculate its size by using its parent to determine how much space is actually available to it and constraining it if necessary. In this case, the Container is being constrained by the SizedBox, which is enforcing a hard limit of 500 pixels by 500 pixels on the Container - no matter how much space the Container requests, it can't get any bigger than that. As such, replacing double.infinity with double.maxFinite doesn't change anything since the parent SizedBox still will only let it get so big.

This will generally be the case, as most layout widgets in Flutter come with some kind of implicit constraint either as part of their function or inherited from their parent (which, ultimately, will be something like the MaterialApp or a Scaffold which constrains their children to the size of the screen). So a lot of the time, children attempting to be bigger than they have space for won't be too much of a problem since Flutter's layout rules are robust enough to prevent them from taking up more space than they are allowed.

There are scenarios in which an infinite constraint can lead to problems, though. Take this very contrived example, for instance:

Column(
  children: [
    Container(
      height: double.infinite,
    ),
  ],
)

Here, we have a Column which has the effect of creating an infinitely-big vertical space for its children to exist in. As its child, it has a Container that requests an infinite height. The problem is clear: how can Flutter constrain an infinitely tall widget within a parent widget that has infinite constraints? And true enough, trying to render this widget produces the following error:

════════ Exception caught by rendering library ═════════════════════════════════ 
The following assertion was thrown during performLayout():
BoxConstraints forces an infinite height.

So if the problem is that an infinitely sized widget can't exist in an infinitely constrained space, what if we changed double.infinity to double.maxFinite in the Container? That would solve the problem, since the Container would no longer be infinitely-sized and Flutter could calculate its constraints normally:

Column(
  children: [
    Container(
      height: double.maxFinite,
      color: Colors.red,
    ),
  ],
)

Success! The infinite height constraint error has gone away!

...We just get this error instead:

════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during layout:
A RenderFlex overflowed by 1.7976931348623157e+308 pixels on the bottom.

So yeah, while the height is being properly calculated now, Column will complain if its children are taller than the available space, and it turns out that roughly one hundred centillion pixels is a bit too tall for modern screens.[citation needed]

(For those curious, the solution here is to wrap the Container in an Expanded, which will take up the visible space in the Column and will once again enforce finite constraints which restrict the Container from taking more space than it is allowed. [Or, you know, you could also not create such an ungodly-sized widget in the first place.])

So in short, in the places where you might use double.infinity in Flutter, there usually isn't any practical difference between double.infinity and double.maxFinite. But on the occasion where there is, you usually just end up trading one problem for a different problem.

All of that being said, much of the Flutter widget library was written with double.infinity being a potential valid input, so in most cases you will either get well-defined behavior or a detailed error message. Usage of double.maxFinite, however, I would expect to be much less directly supported, so while it may work anyway a lot of the time, in the cases where it doesn't, you might not get very clear feedback on what exactly went wrong. So, in general, just use double.infinity where appropriate, and for everywhere else, stick with numerical values that make more sense to our mere mortal minds.

Abion47
  • 22,211
  • 4
  • 65
  • 88
1

To understand the difference between the two, I think it's important to understand how a floating-point number (double) is stored. (Disclaimer: I'm going to 16 bits for explaining ignoring endianness but realistically Dart will use 32 or 64 bits depending on the platform.)

As a frame of reference a 16-bit fixed-point number (int) is stored like the following

 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
 | |___________________________________________|
 |                      |
 v                      v
sign bit            magnitude

The 0th bit is the sign (0 for positive, 1 for negative) and the rest of the bits are the magnitude e.g. the actual number. So -142 would look something like 1000000010001110

In contrast a floating-point number (double) is stored like

 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
 | |__________||_______________________________|
 |          |                  |
 v          v                  v
sign bit   exponent        fraction

The 0th bit is again the sign, the next 4 bits are the exponent and the last bits are the fraction. The formula for the representation is the following

n = (sign) * 2^(exponent - 7) * (1 + fraction)

where the fraction is the sum of the inverse powers of 2. As an example 0100001100000000 represents

sign = 1 // positive because byte 0 is 0
exponent = 8 // 1000 as a decimal
fraction = 0.375 // 0*2^-1 + 1*2^-2 + 1*2^-3 + 0*2^-4 + 0*2^-5 ... 

n = 2.75 // 1 * 2^(8-7) * (1 + 0.375)

In addition the standard will allow for the representation of special values given certain exponents

With an exponent of 0000, we can represent smaller numbers "subnormal" numbers with

n = (sign) * 2^-6 * (0 + fraction)

And with an exponent of 1111, we can represent either infinity or negative infinity if the fraction is zero. For example 0111100000000000 is infinity. If the fraction is not zero, this becomes NaN or Not a Number.

So to answer your question, double.infinity represents 0111100000000000 and double.maxFinite represents 0011111111111111. This should maybe also help to explain some of the other constants like double.nan or double.minPositive which represents 0000000000000001

jpopesculian
  • 712
  • 3
  • 11
0

The difference can be summarized into:

  • I want to be as big as my parent allows (double.infinity)

Some Widgets allow their children to be as big as they want to be

Column, ListView, OverflowBox In that situation use double.infinity

According to the documentation, these are the values assigned to the various double constants.

static const double infinity = 1.0 / 0.0;
static const double negativeInfinity = -infinity;
static const double minPositive = 5e-324;
static const double maxFinite = 1.7976931348623157e+308;

I hope this answers your question

void
  • 12,787
  • 3
  • 28
  • 42