Unofficial LSL reference

User Tools

Login

You are currently not logged in! Enter your authentication credentials below to log in. You need to have cookies enabled to log in.

Login

Forgotten your password? Get a new one: Set new password

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

types:float [2014-06-12 16:34 SLT]
sei Change wording of examples a bit, add another
types:float [2015-09-22 11:28 SLT] (current)
sei missed styling a word
Line 1: Line 1:
-===== Types: float =====+$nav 
 +===== Float type =====
  
-The **float** type represents a number with decimals, as opposed to the [[integer]] type which is for whole numbers only.+The $ty[floattype represents a number with decimals, as opposed to the $lty[integer] type which is for whole numbers only.
  
-Floating-point (**float**) 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 without any digits around it is not a valid floating-point constant.+Floating-point ($ty[float]) 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 without any digits around it is not a valid floating-point constant.
  
-The second format is 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 are: ''​-3.6123e-7'',​ ''​1.2e-27f'',​ ''​5e30''​. The number ''​2e4f''​ is not valid because it has an ''​f''​ and there is no point present. When the power of ten is negative, it means to //divide// by 10 to that power without sign; for example, 3.3e-5 means 3.3/100000 or 0.000033.+The second format is 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 are: ''​-3.6123e-7'',​ ''​1.2e-27f'',​ ''​5e30''​. The number ''​2e4f''​ is not valid because it has an ''​f''​ and there is no point present. When the power of ten is negative, it means to //divide// by 10 to that power without sign; for example, ​''​3.3e-5'' ​means 3.3/100000 or 0.000033.
  
 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''​. 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. It can also have the value minus zero (-0.0) but that one is usually not distinguished from a regular zero. In Mono, a float can also have three special values: ​**Infinity****-Infinity**, and **NaN** ​(Not a Number). In LSO, however, any operation that would result in any of these values will instead cause a math error and stop the script until it's reset.+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. It can also have the value minus zero (-0.0) but that one is usually not distinguished from a regular zero. In Mono, a float can also have three special values: ​$pinf$minf, and $nan (Not a Number). In LSO, however, any operation that would result in any of these values will instead cause a math error and stop the script until it's reset.
  
-=== Automatic type conversion between float and integer ===+==== Automatic type conversion between float and integer ​====
  
-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:+Most places that accept a $ty[float]-type value also accept an $ty[integer]-type value, which will be automatically converted to $ty[floatif necessary. For example:
  
 <code lsl2> <code lsl2>
Line 26: Line 27:
 This can save some space in Mono, because integer constants that get converted to floats take less bytes than float constants. 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 [[functions/llList2Integer]] function, which will implicitly convert a floating-point value in a list to integer. For example:+The opposite is not true in general. If a $ty[floatis used where an $ty[integeris expected, that will generate a compiler or run-time error, depending on the context. An exception is the $lfn[llList2Integer] function, which will implicitly convert a floating-point value in a list to $ty[integer]. For example:
  
 <code lsl2> <code lsl2>
Line 36: Line 37:
  
 // This will display 3 and not cause any errors, because // This will display 3 and not cause any errors, because
-// llList2Integer can be used on a float constant.+// llList2Integer can be used on a float value.
 llOwnerSay((string)llList2Integer([3.0],​ 0)); llOwnerSay((string)llList2Integer([3.0],​ 0));
 </​code>​ </​code>​
  
-=== Precision ===+==== Precision ​====
  
-Float values are [[http://​en.wikipedia.org/​wiki/​Single-precision_floating-point_format|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.+$ty[float] ​values are [[http://​en.wikipedia.org/​wiki/​Single-precision_floating-point_format|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.000000001 can't be distinguished from the number 1.000000002 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. For example, in this format, the number 1.000000001 can't be distinguished from the number 1.000000002 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.
Line 48: Line 49:
 Due to the way floats work, the closer to zero they are the more precision after the decimal point they have, and vice versa. Numbers above 8,388,608.0 or less than -8,​388,​608.0 don't have any decimal places at all, while numbers between -1.0 and 1.0 have at least 6 decimal places, possibly more depending on how close to zero they are. Due to the way floats work, the closer to zero they are the more precision after the decimal point they have, and vice versa. Numbers above 8,388,608.0 or less than -8,​388,​608.0 don't have any decimal places at all, while numbers between -1.0 and 1.0 have at least 6 decimal places, possibly more depending on how close to zero they are.
  
-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 ​list and the list is converted ​to string.+=== Type casting ​float to string ​===
  
-In Mono, the typecast of a float to ''​string'' ​will only store up to 7 significant digits and set the rest to zero, rounding the last digit; for example, ''​1234567890.0''​ will be converted to ''"​1234568000.000000"''​.+When they are typecast to $lty[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, as an exception, when a $lty[vector] (a collection of three floats) or a $lty[rotation] (a collection of four floats) is typecast to $ty[string],​ its floats are converted using five decimal places to the right of the decimal point. This exception does not apply if they are inside a list and the list is converted to string. 
 + 
 +In Mono, the typecast of a $ty[floatto $ty[stringwill only store up to 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): 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):
Line 92: Line 95:
 </​code>​ </​code>​
  
-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:+The float value $nan is translated to the string ''​NaN'',​ no matter the kind; the float value $pinf is translated to the string ''​Infinity''​ and the float value $minf to the string ''​-Infinity''​. This happens also with other functions that convert floats to string (e.g. $lfn[llList2String],​ $lfn[llDumpList2String]),​ except one: $lfn[llList2CSV] translates the float value $nan to the string ''​nan''​ (if it's regular NaN) or ''​-nan''​ (if it's the indeterminate kind of NaN), and translates $pinf and $minf to ''​inf''​ and ''​-inf''​ respectively. 
 + 
 +=== Precision caveats === 
 + 
 +Floats are represented internally in base 2, not in base 10. That leads many people to confusion, because some decimal ​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:
  
 <file lsl2 float-equal.lsl>​ <file lsl2 float-equal.lsl>​
Line 125: Line 132:
 </​file>​ </​file>​
  
-This example prints 0.000000, 0.100000, etc. up to 0.900000 but //not// 1.000000. 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.+This example prints 0.000000, 0.100000, etc. up to 0.900000 but //not// 1.000000. 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 $ty[floattype, and it has to be approximated.
  
-A float can only //exactly// represent fractions which have a power of two as a denominator when simplified. 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 13421773/​134217728 which is approximately ​ 0.1000000015,​ 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. That causes the above problem, and confuses many unaware people who use floating-point math in many languages, not just LSL.+$ty[floatcan only //exactly// represent fractions which have a power of two as a denominator when simplified. For example, 3.75 = 15/4 has a power of two in its denominator and can thus be represented exactly in a $ty[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 13421773/​134217728 which is approximately ​ 0.1000000015,​ 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. That causes the above problem, and confuses many unaware people who use floating-point math in many languages, not just LSL.
  
 In general, it's not a good idea to loop over floats, unless you are aware of the limitations and know that the operations you're doing are safe. One possible solution to the problem in the particular example above is to loop using integers instead, and divide inside the loop, like this: In general, it's not a good idea to loop over floats, unless you are aware of the limitations and know that the operations you're doing are safe. One possible solution to the problem in the particular example above is to loop using integers instead, and divide inside the loop, like this:
Line 147: Line 154:
 This example will print 0.000000, 0.100000, 0.200000, etc. up to 1.000000 inclusive. This example will print 0.000000, 0.100000, 0.200000, etc. up to 1.000000 inclusive.
  
-Another possible solution is to use a step that is a power of 2, like 0.125, instead of 0.1, because that step is not affected by rounding errors, since it can be represented exactly. This will work as expected:+Another possible solution is to use a step that is a power of 2, like 0.125 (1/8), instead of 0.1, because that step is not affected by rounding errors, since it can be represented exactly. This will work as expected:
  
 <file lsl2 float-loop-8steps.lsl>​ <file lsl2 float-loop-8steps.lsl>​
Line 162: Line 169:
 } }
 </​file>​ </​file>​
 +
 +That loop will print 0.125000, 0.250000, 0.375000, etc. up to 1.000000 inclusive.
 +