6
\$\begingroup\$

Format

The format is a 64 bit value x representing a IEEE-754 double-precision binary floating-point number (a.k.a "a double"). x is assumed to represent a real number (not a NaN or Infinity).

Goal

The goal is to print/output the shortest string containing a decimal representation for which x is the closest IEEE 754 representation. Scientific notation is allowed. You can choose whether or not to allow a missing 0 in front of the ".".

x = 0x4000CCCCCCCCCCCD could be represented as "2.10000000000000008881784197001" but also as "2.1", which is much shorter. x = 0x3DDB7CDFD9D7BDBB can be represented as "1e-10" which is much shorter than alternative representation "1.0000000000000000364321973155E-10"

Rules

In addition to being correct, the program must run in a reasonable amount of time on a modern CPU, ruling out some forms of exhaustive search.

This being code-golf entries should be judged by concision, but voters should feel free to attribute points based on a discretionary judgment of elegance, and efficiency.

A few test cases

In these test cases, I represent the input as an hexadecimal number to make it clear that a IEEE 754 double 64 is passed. You can pass it as a double if the language supports it, or as a 64 bit integer, at your convenience.

f(0x4000CCCCCCCCCCCD) == "2.1" 
f(0x3DDB7CDFD9D7BDBB) == "1e-10"
f(0x40934A456D5CFAAD) == "1234.5678" // N.B. not 1.2345678e3
f(0x3FEFFFFFFAA19C47) == "0.99999999"
f(0xC088A8CF13CEE9DD) == "-789.101112"`
\$\endgroup\$
  • \$\begingroup\$ If by "C will parse" you're talking about C compilers, the standard says that "the result is either the nearest representable value, or the larger or smaller representable value immediately adjacent to the nearest representable value, chosen in an implementation-defined manner." If you're talking about scanf there is still implementation-specific behaviour with regards to rounding. To make this question answerable, I think it needs to be rewritten to talk not about C's parsing but about the string-to-double conversion of the language used in the answer. \$\endgroup\$ – Peter Taylor Dec 2 '14 at 12:49
  • \$\begingroup\$ The cleanest way of expressing it is probably to require that the answer be a function (whose length is counted for the score) and also include a test framework (length not counted) which demonstrates that the string returned by the function round-trips to the original value. \$\endgroup\$ – Peter Taylor Dec 2 '14 at 12:51
  • \$\begingroup\$ OK I'll edit in a few hours with a few test cases. The requirement isn't compiler specific, it's a standard. But I'll replace with what the standard actually says... \$\endgroup\$ – Arthur B Dec 2 '14 at 13:10
  • 1
    \$\begingroup\$ Can I submit an entry for Python 3.1 with f=str? Because Python will always choose the shortest representation. \$\endgroup\$ – kennytm Dec 2 '14 at 15:51
  • 2
    \$\begingroup\$ I don't think this challenge is worthwhile unless solutions that are effectively calls to printf are disqualified. \$\endgroup\$ – hobbs Dec 6 '14 at 7:17
5
\$\begingroup\$

C - 47 45 characters

Counting just the function f, and not including #includes and main.

In C, the %g fprintf specifier will always choose the shortest format. Add a precision specifier equal to the usual maximum precision of double and a bit of casting hocus-pocus to coerce int64 to double and Bob's your uncle.

#include <assert.h>
#include <stdint.h>
#include <stdio.h>

// The following line is what I'm counting...
f(int64_t u){printf("%.14g\n",*(double*)&u);}

int main()
{
    assert(sizeof(int64_t) == sizeof(double));

    f(0x4000CCCCCCCCCCCD);
    f(0x3DDB7CDFD9D7BDBB);
    f(0x40934A456D5CFAAD);
    f(0x3FEFFFFFFAA19C47);
    f(0xC088A8CF13CEE9DD);

    return 0;
}

Output is as per specs:

2.1
1e-10
1234.5678
0.99999999
-789.101112

Summary: C rocks!

\$\endgroup\$
  • 1
    \$\begingroup\$ You can save a character by using uint64_t in place of long long, or even 2 characters if you use int64_t. \$\endgroup\$ – Paul R Dec 2 '14 at 17:32
  • \$\begingroup\$ @PaulR - Thank you, will try that out! \$\endgroup\$ – user15259 Dec 2 '14 at 18:19
  • \$\begingroup\$ You may pass the test set, but not the general requirement. %g picks the shortest of %f or %e, but %f will not always reproduce all the digits necessary to represent the number. \$\endgroup\$ – Arthur B Dec 15 '14 at 20:38
  • \$\begingroup\$ @ArthurB - %14g will propagate the 14 to %f or %e. /YR \$\endgroup\$ – user15259 Dec 15 '14 at 21:23
  • 1
    \$\begingroup\$ Maximum (round-tripping) precision of double is 15 decimal places, and to print unambiguous representation of every given binary number you need up to 17. \$\endgroup\$ – Ruslan Jan 26 '17 at 19:19
0
\$\begingroup\$

Common Lisp - 138 bytes

 (ql:quickload'(ieee-floats cl-ppcre))(lambda(x)(#1=ppcre:regex-replace"e0$"(#1#"\\.?d"(format()"~a"(ieee-floats:decode-float64 x))"e")""))

(138 = 101 for the actual function + 37 for quickload'ing libraries)

Ungolfed

The default representation of floating-points in CL is already quite close to the requested output. However, there are some discrepancies. For example, double-precisions numbers are written with a d exponent character (e.g., 2.10d0). That is why I modify the output of the formatted string with regexes:

(ql:quickload '(ieee-floats cl-ppcre))
(lambda (x)
  (cl-ppcre:regex-replace  ; remove "e0" at end of string
     "e0$" 
     (cl-ppcre:regex-replace ; change ".d" and "d" to "e"
       "\\.?d"
       (format nil "~a" (ieee-floats:decode-float64 x))
       "e")
     ""))

Example

;; NB: * is set by the REPL to the last returned value. 
;; Here, we store the previously defined lambda:
(defparameter *fun* *) 

;; Test cases
(defparameter *sample*
  '(#x4000CCCCCCCCCCCD
    #x3DDB7CDFD9D7BDBB
    #x40934A456D5CFAAD
    #x3FEFFFFFFAA19C47
    #xC088A8CF13CEE9DD))

;; Default representation
(mapcar #'ieee-floats:decode-float64 *sample*)
=> (2.1d0 1.d-10 1234.5678d0 0.99999999d0 -789.101112d0)

;; Custom conversion
(mapcar *fun* *sample*)
=> ("2.1" "1e-10" "1234.5678" "0.99999999" "-789.101112")

Round-trip

Here, we convert the printed representations back to IEEE-754 doubles.

;; read values as double-precision numbers
(let ((*read-default-float-format* 'double-float))
  (mapcar #'read-from-string *))
=> (2.1 1.e-10 1234.5678 0.99999999 -789.101112)

;; Encode
(mapcar #'ieee-floats:encode-float64 *)
=> (4611911198408756429 4457293557087583675 4653144502051863213 4607182418709945415 13873524259458836957)

;; Check
(equal * *sample*)
=> T
\$\endgroup\$
0
\$\begingroup\$

Julia, 20 characters

r(h)=hex2num(hex(h))

Function to test the results for correctness:

function decrep()
    tests=[0x4000CCCCCCCCCCCD 0x3DDB7CDFD9D7BDBB 0x40934A456D5CFAAD 0x3FEFFFFFFAA19C47 0xC088A8CF13CEE9DD]
    results=[2.1 1e-10 1234.5678 0.99999999 -789.101112]

    for i=1:length(tests)
        println("input   : $(hex(tests[i]))")
        println("result  : $(r(tests[i]))")
        println("expected: $(results[i])")
        println("$(r(tests[i]) == results[i])")
    end
end

Executing the test function:

julia> decrep()
input   : 4000cccccccccccd
result  : 2.1
expected: 2.1
true
input   : 3ddb7cdfd9d7bdbb
result  : 1.0e-10
expected: 1.0e-10
true
input   : 40934a456d5cfaad
result  : 1234.5678
expected: 1234.5678
true
input   : 3feffffffaa19c47
result  : 0.99999999
expected: 0.99999999
true
input   : c088a8cf13cee9dd
result  : -789.101112
expected: -789.101112
true

If the output absolutely has to be a string, then the solution would be:

s(h)=string(hex2num(hex(h)))

with 28 characters.

julia> h=0x4000cccccccccccd
0x4000cccccccccccd

julia> s(h)=string(hex2num(hex(h)))
s (generic function with 1 method)

julia> s(h)
"2.1"

Summary: Julia rocks, too ;)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.