3

If I have a float(5) column, why does 7.89 get rounded to 7.9 but 12.79 gets rounded to 13, not 12.8?

Binary forms are as follows for 3 examples:

 7.89    0111.01011001  ------ round to------\> 7.9   0111.01001
12.79    01100.01001111 ------ round to------\> 12.8  01100.1
 1.23    1.010111       ------ round to------\> 1.2   1.1
CREATE TABLE t1 ( item FLOAT(5) );
INSERT INTO t1 VALUES ( 7.89 );
INSERT INTO t1 VALUES ( 12.79 );
INSERT INTO t1 VALUES ( 1.23 );
SELECT ITEM FROM T1;

ITEM
----
 7.9
  13
 1.2
13
  • 3
    Alternatively, if Oracle were using IEEE-754 binary64 (“double precision”) to represent the values but choosing to round them to two decimal digits for display (because five bits gives you approximately 1.50515 decimal digits of information, which we could round to two), that would explain the results of 7.9, 13, and 1.2. Commented Aug 5 at 0:41
  • 1
    @EricPostpischil that feels Oracle-ish would make a great Answer! Commented Aug 5 at 0:47
  • 2
    This is a real mystery. The page referred to by @gsalem is evidently the same page Tayebeh Zarif has been looking at, because the examples — 7.89, 12.79, and 1.23 — are the same. The page makes it clear that the precision 5 is interpreted as a number of binary bits. Yet as Eric's analysis shows, there is no way that the decimal value 7.89, converted to binary and rounded to 5 bits of precision, and converted back to decimal, could yield 7.9 — the valid possibilities are 7.75, 7.8, or (properly rounded) 8.0. And similarly for the other two examples. What is Oracle up to here? Commented Aug 5 at 14:10
  • 1
    Have you tested this with the actual software, not just looked the examples from the documentation? Commented Aug 5 at 14:45
  • 1
    The hypothesis that Oracle is working internally with higher precision and then rounding the results makes a certain amount of sense. (Implementing arbitrary N-bit floating point arithmetic internally would be a pain, and slow.) The documentation even refers to multiplying N by 0.30103 to convert from binary to decimal precision. This could explain 123.45 being displayed as 120, also. A different page (not Oracle's) claims that SQL types FLOAT(1) through FLOAT(24) map to IEEE754 single precision, while 25-53 map to double. Commented Aug 5 at 16:14

2 Answers 2

1

This answer presents a hypothesis fitting available information (excluding inconsistencies in the documentation linked below), that FLOAT(x) is a floating-point type with ⌈x log10 2⌉ decimal digits in its significand.

The examples in the question appear to come from this page pointed out by user gsalem, or a version of it.

First, we note the page discusses a NUMBER type, parameterized by a precision p and a scale s. The precision p is a number of decimal digits. Specifically, the page says it is “the maximum number of significant decimal digits.” (The page suggests the actual storage may use base 100, where it mentions “precision of up to 20 base-100 digits.” Software might provide decimal behavior while using base 100. For example, with three decimal digits of precision, the first base-100 digit could be any digit from 0 to 99, while the second digit would be limited to 0, 10, 20, 30, 40, 50, 60, 70, 80, or 90.)

The page then shows a number of examples that are consistent with the above. I note it appears to be a fixed-point format; the scale s is not a bound on a floating exponent but is a fixed exponent for the format. For example, converting 123.89 to NUMBER(6, -2) yields 100, which is consistent with a format containing six significant digits with the lowest in the 102 position (the - of -2 meaning to the left of the decimal point). That is, this format would have decimal numerals in the form abcdef00, so the closest it can get to 123.89 is 00000100, or 100.

Next, the page discusses the FLOAT type, which it says is a “subtype” of NUMBER. It also says “It can be specified with or without precision, which has the same definition it has for NUMBER” (emphasis added). But this is clearly false: If FLOAT(5) meant 5 decimal digits, as it did for NUMBER, then 1.23, 7.89, 12.79, and 123.45 could all be represented in FLOAT(5), but the examples show they are not.

Cryptically, the page says “To convert from binary to decimal precision, multiply n by 0.30103.” It has neither introduced n nor explained what binary precision we are converting from, nor where the result of this conversion is used. We will come back to this.

The page gives us these examples:

Original Value Converted to NUMBER(5, 2) Converted to FLOAT(5)
1.23 1.23 1.2
7.89 7.89 7.9
12.79 12.79 13
123.45 123.45 120

As explanation, the page states “the FLOAT value must be truncated so that its significant digits do not require more than 5 binary digits. Thus 123.45 is rounded to 120, which has only two significant decimal digits, requiring only 4 binary digits.” However, this explanation is inadequate. Converting 123.45 to no more than 5 binary digits would allow the closer value 124, which is 11111002 in binary, which has exactly 5 significant binary digits.

Further, converting 7.89 to no more than 5 binary digits cannot yield 7.9. The closest representable values are 1000.02 (decimal 8) and 111.112 (decimal 7.75).

Therefore, we conclude this discussion of binary digits as a limit explaining the examples is rubbish.

I propose this hypothesis: FLOAT(x) provides a type with ⌈x log10 2⌉ decimal digits (in code, ceil(x*log(2)/log(10))). Thus FLOAT(5) provides ⌈5 log10 2⌉ = ⌈1.505…⌉ = 2 decimal digits. Observe this fits the examples given in the page: 1.23, 7.89, 12.79, and 123.45 each map to the closest decimal numeral with two significant digits, 1.2, 7.9, 13, and 120.

Also note this is not a fixed-point format. Each of the numbers has two significant decimal digits, but they appear at varying positions: 100 to 10−1 in the first two, 101 to 100 in the third, and 102 to 101 in the fourth. This is a floating-point format, which is consistent with the page’s statement that “Scale cannot be specified, but is interpreted from the data” and inconsistent with the statement that “The FLOAT data type is a subtype of NUMBER.” It is not a subtype but is a similar format (decimal with a fixed number of digits) with a floating exponent instead of a fixed exponent.

Sign up to request clarification or add additional context in comments.

2 Comments

FLOAT is a subtype of NUMBER - explicitly, in the SYS.STANDARD package - and the internal representation of both is identical, with the sign, base-100 exponent and then up to 20 bytes of base-100 mantissa digits. Specifying NUMBER(6, -2) or FLOAT(5) causes Oracle to round/truncate/adjust to the specified precision/scale but then it's just a generic number. Your interpretation of what it actually does with FLOAT, and your rubbishing of the docs (which are pretty much unchanged since 11g, and worse in 10g) all seems right though...
Thank you .Your explanation was complete and comprehensive ,the answer is exactly related to this logic: FLOAT(x) provides a type with ⌈x log10 2⌉ decimal digits.
0

The FLOAT data-type is not a binary data-type; the values are stored in a proprietary format as pairs of decimal digits (base-100) so that an exact numeric representation of the number can be stored, up to a given precision.


Comparing the NUMBER data-types:

  • FLOAT[b] stores up to d = ceil(log(10,2)*b) decimal digits where values have up to d significant figures and a variable scale.
  • NUMBER[d] stores up to d digits where values have a precision of d decimal digits and a scale of 0.
  • NUMBER[*,d] stores an unknown number of digits with a scale of d.

Looking at how numbers are represented in FLOAT columns if varying lengths:

CREATE TABLE table_name (
  f1 FLOAT(1),
  f2 FLOAT(2),
  f3 FLOAT(3),
  f4 FLOAT(4),
  f5 FLOAT(5),
  f6 FLOAT(6),
  f7 FLOAT(7),
  f8 FLOAT(8),
  n5 NUMBER(5),
  n_5 NUMBER(*,5)
);

INSERT INTO table_name
  SELECT 1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1),
         1.23456789 * POWER(10, LEVEL-1)
 FROM DUAL
 CONNECT BY LEVEL <= 5;

Then querying the values and the DUMP showing the underlying binary values settled in the database:

SELECT f1, DUMP(f1),
       f2, DUMP(f2),
       f3, DUMP(f3),
       f4, DUMP(f4),
       f5, DUMP(f5),
       f6, DUMP(f6),
       f7, DUMP(f7),
       f8, DUMP(f8),
       n5, DUMP(n5),
       n_5, DUMP(n_5)
FROM   table_name;

Outputs:

F1 DUMP(F1) F2 DUMP(F2) F3 DUMP(F3) F4 DUMP(F4) F5 DUMP(F5) F6 DUMP(F6) F7 DUMP(F7) F8 DUMP(F8) N5 DUMP(N5) N_5 DUMP(N_5)
1 Typ=2 Len=2: 193,2 1 Typ=2 Len=2: 193,2 1 Typ=2 Len=2: 193,2 1.2 Typ=2 Len=3: 193,2,21 1.2 Typ=2 Len=3: 193,2,21 1.2 Typ=2 Len=3: 193,2,21 1.23 Typ=2 Len=3: 193,2,24 1.23 Typ=2 Len=3: 193,2,24 1 Typ=2 Len=2: 193,2 1.23457 Typ=2 Len=5: 193,2,24,46,71
10 Typ=2 Len=2: 193,11 10 Typ=2 Len=2: 193,11 10 Typ=2 Len=2: 193,11 12 Typ=2 Len=2: 193,13 12 Typ=2 Len=2: 193,13 12 Typ=2 Len=2: 193,13 12.3 Typ=2 Len=3: 193,13,31 12.3 Typ=2 Len=3: 193,13,31 12 Typ=2 Len=2: 193,13 12.34568 Typ=2 Len=5: 193,13,35,57,81
100 Typ=2 Len=2: 194,2 100 Typ=2 Len=2: 194,2 100 Typ=2 Len=2: 194,2 120 Typ=2 Len=3: 194,2,21 120 Typ=2 Len=3: 194,2,21 120 Typ=2 Len=3: 194,2,21 123 Typ=2 Len=3: 194,2,24 123 Typ=2 Len=3: 194,2,24 123 Typ=2 Len=3: 194,2,24 123.45679 Typ=2 Len=6: 194,2,24,46,68,91
1000 Typ=2 Len=2: 194,11 1000 Typ=2 Len=2: 194,11 1000 Typ=2 Len=2: 194,11 1200 Typ=2 Len=2: 194,13 1200 Typ=2 Len=2: 194,13 1200 Typ=2 Len=2: 194,13 1230 Typ=2 Len=3: 194,13,31 1230 Typ=2 Len=3: 194,13,31 1235 Typ=2 Len=3: 194,13,36 1234.56789 Typ=2 Len=6: 194,13,35,57,79,91
10000 Typ=2 Len=2: 195,2 10000 Typ=2 Len=2: 195,2 10000 Typ=2 Len=2: 195,2 12000 Typ=2 Len=3: 195,2,21 12000 Typ=2 Len=3: 195,2,21 12000 Typ=2 Len=3: 195,2,21 12300 Typ=2 Len=3: 195,2,24 12300 Typ=2 Len=3: 195,2,24 12346 Typ=2 Len=4: 195,2,24,47 12345.6789 Typ=2 Len=6: 195,2,24,46,68,90

Which show that:

  • FLOAT(1), FLOAT(2) and FLOAT(3) all store 1 significant decimal digit with identical values;
  • FLOAT(4), FLOAT(5) and FLOAT(6) all store 2 significant decimal digits with identical values;
  • FLOAT(7) and FLOAT(8) all store 3 significant decimal digits with identical values.

fiddle


Note: If you want to store values as binary floating point numbers then use the BINARY_FLOAT or BINARY_DOUBLE data-types.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.