You are currently not logged in! Enter your authentication credentials below to log in. You need to have cookies enabled to log in.
This is an old revision of the document!
The float type represents a number with decimals, as opposed to the integer type which is for whole numbers only.
Floating-point constants can be entered in two formats. The first is by just writing the number with its sign, the decimal point (mandatory for the constant to actually be a float and not an integer), and the decimals. For example, 3.7
or -5.4164
are valid float constants. Neither the part to the left of the point nor the part to the right of the point are mandatory (e.g. 37.
or -.25
are valid), but a point alone is not valid.
The second format is in scientific notation. That means to write a number (with or without decimals, and with an optional minus sign) that is to be multiplied by a power of ten, then an e
(upper or lower case, doesn't matter), then the power of ten (optionally with a plus or minus sign). It can be optionally followed by an f
(upper or lower case) but that extra f
has no effect, and it is only allowed if there's a point present. For example: 2e4
and 2.0E+4F
are float constants that represent the same number, namely 2.0 times 10 to the 4th power, which can also be written as 20000.0; other examples: -3.6123e-7
, 1.2e-27f
, 5e30
. The number 2e4f
is not valid because there is no point present.
This so called scientific notation allows writing very big and very small numbers without having to write that many digits. For example, imagine having to write 5.4e30
as 5400000000000000000000000000000.0
.
The range of a float is approximately from -3.4028234e38 to 3.4028234e38. The smallest positive number that it can represent is approx. 1.4e-45. In Mono, a float can also have three special values: Infinity, -Infinity, and NaN (Not a Number).
Most places that accept a float-type value also accept an integer-type value, which will be automatically converted to float if necessary. For example:
// 3 is an integer constant, but it is automatically converted to float float x = 3; // llSetTimerEvent accepts a float parameter, but this will work; integer j = 2; llSetTimerEvent(j);
This can save some space in Mono, because integer constants that get converted to floats take less bytes than float constants.
The opposite is not true in general. If a float is used where an integer is expected, that will generate a compiler or run-time error, depending on the context. An exception is the llList2Integer function, which will implicitly convert a floating-point value in a list to integer. For example:
integer a = 3.0; // will give a type mismatch error when compiling // PRIM_MATERIAL should be followed by an integer. // The following line will cause a run-time error when executed. llSetPrimitiveParams([PRIM_MATERIAL, 1.0]); // This will display 3 and not cause any errors, because // llList2Integer can be used on a float constant. llOwnerSay((string)llList2Integer([3.0], 0));
Float values are single-precision floats. An implication is that their precision is 6 to 9 significant digits, that is, total digits starting from the leftmost non-zero digit.
For example, in this format, the number 1.0000000001 can't be distinguished from the number 1.0000000002 because it requires 10 significant digits to distinguish them, and there are not enough significant digits in the float type. However, the number 1.00001 can be distinguished from the number 1.00002 because it has 6 significant digits.
Due to the way floats work, the closer to zero they are the more precision after the decimal point they have. Numbers above 8,388,608 or less than -8,388,608 don't have any digit after the decimal point at all, while numbers less than 1 and greater than -1 have at least 6 extra digits.
When they are typecast to string, floats are always translated with all of their digits to the left of the decimal point, and six decimal places to the right, rounded. However, when a vector (a collection of three floats) or a rotation (a collection of four floats) is typecast to string
, its floats are converted using five decimal places to the right of the decimal point. This does not happen if they are inside a list and the list is converted to string.
In Mono, the typecast of a float to string
will only store 7 significant digits and set the rest to zero, rounding the last digit; for example, 1234567890.0
will be converted to "1234568000.000000"
.
Some examples will illustrate these rules (the results from these examples are generated with Mono; output in LSO will be different because the float formatting functions differ):
llOwnerSay((string)1.); // Prints 1.000000 llOwnerSay((string)3e38); // Prints 300000000000000000000000000000000000000.000000 llOwnerSay((string)1e-6); // Prints 0.000001 llOwnerSay((string)1e-7); // Prints 0.000000 llOwnerSay((string)5e-7); // Prints 0.000001 because of the rounding llOwnerSay((string)3.1415926535897932384626433832); // Prints 3.141593 llOwnerSay((string)<1.0, 2.0, 3.0>); // Prints <1.00000, 2.00000, 3.00000> // (note each number displayed with 5 decimal places, not 6) llOwnerSay((string)<1.0, 2.0, 3.0, 4.0>); // Prints <1.00000, 2.00000, 3.00000, 4.00000> // (note each number displayed with 5 decimal places, not 6) llOwnerSay((string)[<1.0, 2.0, 3.0>]); // In this case, it's a list what gets converted to string. // When a list is converted to string, the floats in the vectors and rotations // that it contains are converted with 6 decimal places instead of 5. // The output is thus: <1.000000, 2.000000, 3.000000> llOwnerSay(llList2String([<1.0, 2.0, 3.0>], 0)); // Same as above. llList2String applied to a vector element will convert to // string using 6 decimal places too.
Floats are represented internally in base 2, not in base 10. That leads many people to confusion, because some numbers can't be represented accurately in base 2, and rounding errors occur. It's not a bug; it's a limitation of the type, and a necessary compromise. For example:
default { state_entry() { float a = 0.6 + 0.1; float b = 0.7; if (a == b) llOwnerSay("equal"); else llOwnerSay("different"); } }
The code above prints "different", because the addition introduces rounding errors. A frequent instance of this problem arises when counting from 0 to 1 in increments of 0.1. The following example illustrates a typical case:
default { state_entry() { float f; for (f = 0; f <= 1; f += 0.1) { llOwnerSay((string)f); } } }
This example prints 0.000000, 0.100000, etc. up to 0.900000 but not 1.000000 because it never reaches it. Due to accumulated rounding error, the actual final value is approximately 1.00000012 instead of 1, which is slightly larger than 1 and thus doesn't meet the condition for f <= 1. That is unfortunate but expected, because 0.1 can not be represented exactly in a float type, and it has to be approximated.
A float can only represent exactly fractions which have a power of two as a denominator. For example, 3.75 = 15/4 has a power of two in its denominator and can thus be represented exactly in a float. Same happens with 3.78125 = 121/32. However, 0.1 = 1/10 can't be represented in that form, because 10 is not a power of two. Instead, it's represented with the approximation 838861/8388608 which is approximately 0.100000024, and when adding many of these together, rounding does not always get the desired result, and it's expectable that sooner or later, the obtained result will differ from the expected one.