Change:
We need to be able to parse the output of printf's %a for proper support of mathematical constants without loss of precision (i.e. 0x1.fffffffffffffp+1023_f64 syntax)
I have played with this issue and concluded that this unfortunately revives issue . The current lexer prohibits any alpha character after because of it. We may choose selectively allow them when the pattern is found, but it seems too inconsistent and may require infinite lookahead if we want to elide an inconsistency.
I suggest requiring a digit or 0x or 0b after the dot. This avoids the collision and (curiously) lets you switch radix mid-literal if you choose. And it is only slightly uglier than what C99 makes you write.
Or we can require only zeroes after the dot. (e.g. instead of )
I don't believe this is backwards incompatible, renominating.
accepted for feature-complete milestone
visited for triage dating from 2013-07-15. Have we actually settled on a syntax here? This seems like it might be a nice easy task for a new contributor if we can hand them a clear specification for the syntax; but if the syntax is still in the design phase, then that claim is less true.
I think we haven't quite nailed the syntax but I should point out it'd be necessary to avoid colliding with suffixes (some of which start with f, a hex digit), so I think if we do this it should only work for specifying the mantissa, and only when combined with a full exponent (in decimal).
This has yet to be implemented.
Not 1.0
Hexadecimal floats are quite popular among C math libraries. And I would love to use them in Rust as well. I see that hexadecimal float has been implemented in rust as an extension, then moved to a separate crate and now it is at the and fails to compile with nightly rust. What is the future of this feature?
Also interested in this. I'm looking to implement a replayable physics system for a game, which requires consistent results across computers. Hex float literals would allow me to write bit-exact floating-point values in tests and constants. At the very least, a statement why this feature is being deprecated would be appreciated, as this thread is the first result I get for "rust hex float literal".
The situation is a bit icky at the moment. Procedural macros are going through a revamp right now (), and so it would at least be a waste of time to keep up-to-date while this is happening. But I don't know what the story will be after that.
If my understanding is correct, hexadecimal floats in C/C++ are there because decimal floats are not guaranteed to round correctly [1]. In Rust, however, after ---I suspect this is not necessarily intentional though!---almost all decimal floats ( describes edge cases that are practically irrelevant) should round to the nearest, so you can just give rustc an appropriate number (say, 30) of fractional digits and will get the correctly rounded number. This would be a "practical" answer for now. One thing for which I still think hexadecimal floats are relevant is a conversion from C/C++. You wouldn't want to convert all hexadecimal floats to decimal yourself :) I have recently written an experimental procedural macro that does exactly this, converting hexadecimal floats to decimal floats (that rustc can understand), but had been reluctant to release one because the status quo was not actually guaranteed so far---it's a pure luck in my opinion. If there is a way to construct a float with exact bit pattern only from constexprs I will adapt that. [1] For example, ISO C99 only requires that decimal floats should be converted to a representable number within ±1.5 ulps ("the result is either the nearest representable value, or the larger or smaller representable value immediately adjacent to the nearest representable value").
sooner or later I'd like to fix by correctly translating C hex floats to Rust, so I'd like to understand your comment better. I haven't done enough reading on floating-point issues to understand what's involved here yet. Are you saying that, modulo Rust compiler bugs, every hex-float literal can be converted to a decimal float literal that the Rust compiler will convert to the same bit-pattern? If so, I'd be happy to have Corrode do that conversion. Can you recommend a reference I should read for an algorithm to do that conversion correctly? (A pointer to your procedural macro would be great, but ideally I'd like to have a paper or book to cite too.) That said, I gather the exact decimal version of a hex float may take significantly more digits (right?), so perhaps the hex-float version is easier to read and understand, at least for people who care enough about numeric precision to use them. If hex floats are the human-preferred form for these numbers, I'd argue that Rust should support them. So IMO, more feedback from people who have used hex floats would help here.
My belief is yes. You don't have to implement that, because the current Rust compiler and standard library implements all necessary algorithms (which is harder one :-). If you really need references see the following: The conversion from binary to decimal ("flt2dec", ) is a hybrid of [Dragon4] and [Grisu3]. [Dragon4]: Burger, R. G. and Dybvig, R. K. 1996. Printing floating-point numbers quickly and accurately. SIGPLAN Not. 31, 5 (May. 1996), 108-116. [Grisu3]: Florian Loitsch. 2010. Printing floating-point numbers quickly and accurately with integers. SIGPLAN Not. 45, 6 (June 2010), 233-243. The conversion from decimal to binary ("dec2flt", ) is a couple of algorithms described by [Clinger]. (I haven't implemented them myself so my knowledge for them is limited, however.) [Clinger]: William D. Clinger. 1990. How to read floating point numbers accurately. SIGPLAN Not. 25, 6 (June 1990), 92-101. For the record, my implementation is (published right now, not yet in ). Feel free to pick up. Not exactly, for example, = . I have no opinion on whether Rust should support hex floats or not.
I've now formally published hexf to . I think you can probably use hexf-parse for your work? (The syntax without underscores should be almost same to C99 non-terminal sans optional .) Edit: Aargh I missed one case. should be valid but hexf doesn't recognize it; probably it is easy to account for, though.
Similar use-case for me too; I would like to be able to generate rust code from a compiler, without loss of precision for float literals.
Can we have rust branch for hexadecimal as default base for ascii810 . Any how 0 was invented for implementation of hexadecimal no system in India . Hexadecimal is easy vith 3 good things are counters , fingers are countables . So 8+8=10=4*4 is (8+8) / (8+8) % complete dizitization, decimal is incomplete dizitization (5+5)/(8+8) % . hexadecimal 10 , 8 , 4, 2, 1, 0.8 , ... series remains even till 1. In decimal 10, 5, .. It is not even.
I also need binary floats...
So I'm a bit confused by the status quo here. I'd like to use as fragment specifier in a macro to filter s (as you can't write out s only with one literal), since writing out (with from the crate) can be quite lengthy as a unit specifier in a GUI toolkit. But from this issue... will this ever be implemented?
Is it possible to allow the hexadecimal literal in procedure macros? I want to allow uses to create arbitrary precision binary float number losslessly in , however the syntax is rejected by the lexer before proc macros are executed. If this could be done, then at least we can have proc macros libraries to support the hexadecimal literals. (Just like arbitrary postfixes for integer literals are permitted by lexer, but not allowed in the real code)
For anyone (else) stumbling on this issue, wanting some tooling related to hex floats, if not literals: mentioned above that will generate strings from floats for you. I published yesterday because my needs involve doing the reverse and generating hex floats from and , it adds a to your floats. apparently does both parsing and printing (Eric and I were apparently working on roughly the same thing and published our crates on the same day!).

Discussion:
TARGET_CRATES := std extra green rustuv native flate arena glob term semver  uuid serialize sync getopts collections num test time <del> HOST_CRATES := syntax rustc rustdoc fourcc </del> <ins> HOST_CRATES := syntax rustc rustdoc fourcc hexfloat </ins> CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc

DEPS_getopts := std DEPS_collections := std DEPS_fourcc := syntax std <ins> DEPS_hexfloat := syntax std </ins> DEPS_num := std DEPS_test := std extra collections getopts serialize term DEPS_time := std serialize

<ins> // Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. /*! Syntax extension to create floating point literals from hexadecimal strings Once loaded, hexfloat!() is called with a string containing the hexadecimal floating-point literal, and an optional type (f32 or f64). If the type is omitted, the literal is treated the same as a normal unsuffixed literal. # Examples To load the extension and use it: ```rust,ignore #[phase(syntax)] extern crate hexfloat; fn main() { let val = hexfloat!("0x1.ffffb4", f32); } ``` # References * [ExploringBinary: hexadecimal floating point constants] (http://www.exploringbinary.com/hexadecimal-floating-point-constants/) */ #[crate_id = "hexfloat#0.10-pre"]; #[crate_type = "rlib"]; #[crate_type = "dylib"]; #[license = "MIT/ASL2"]; #[feature(macro_registrar, managed_boxes)]; extern crate syntax; use syntax::ast; use syntax::ast::Name; use syntax::codemap::{Span, mk_sp}; use syntax::ext::base; use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr}; use syntax::ext::build::AstBuilder; use syntax::parse; use syntax::parse::token; #[macro_registrar] pub fn macro_registrar(register: |Name, SyntaxExtension|) { register(token::intern("hexfloat"), NormalTT(~BasicMacroExpander { expander: expand_syntax_ext, span: None, }, None)); } //Check if the literal is valid (as LLVM expects), //and return a descriptive error if not. fn hex_float_lit_err(s: &str) -> Option<(uint, ~str)> { let mut chars = s.chars().peekable(); let mut i = 0; if chars.peek() == Some(&'-') { chars.next(); i+= 1 } if chars.next() != Some('0') { return Some((i, ~"Expected '0'")); } i+=1; if chars.next() != Some('x') { return Some((i, ~"Expected 'x'")); } i+=1; let mut d_len = 0; for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; d_len += 1;} if chars.next() != Some('.') { return Some((i, ~"Expected '.'")); } i+=1; let mut f_len = 0; for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; f_len += 1;} if d_len == 0 && f_len == 0 { return Some((i, ~"Expected digits before or after decimal point")); } if chars.next() != Some('p') { return Some((i, ~"Expected 'p'")); } i+=1; if chars.peek() == Some(&'-') { chars.next(); i+= 1 } let mut e_len = 0; for _ in chars.take_while(|c| c.is_digit()) { chars.next(); i+=1; e_len += 1} if e_len == 0 { return Some((i, ~"Expected exponent digits")); } match chars.next() { None => None, Some(_) => Some((i, ~"Expected end of string")) } } pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult { let (expr, ty_lit) = parse_tts(cx, tts); let ty = match ty_lit { None => None, Some(Ident{ident, span}) => match token::get_ident(ident).get() { "f32" => Some(ast::TyF32), "f64" => Some(ast::TyF64), _ => { cx.span_err(span, "invalid floating point type in hexfloat!"); None } } }; let s = match expr.node { // expression is a literal ast::ExprLit(lit) => match lit.node { // string literal ast::LitStr(ref s, _) => { s.clone() } _ => { cx.span_err(expr.span, "unsupported literal in hexfloat!"); return base::MacResult::dummy_expr(sp); } }, _ => { cx.span_err(expr.span, "non-literal in hexfloat!"); return base::MacResult::dummy_expr(sp); } }; { let err = hex_float_lit_err(s.get()); match err { Some((err_pos, err_str)) => { let pos = expr.span.lo + syntax::codemap::Pos::from_uint(err_pos + 1); let span = syntax::codemap::mk_sp(pos,pos); cx.span_err(span, format!("invalid hex float literal in hexfloat!: {}", err_str)); return base::MacResult::dummy_expr(sp); } _ => () } } let lit = match ty { None => ast::LitFloatUnsuffixed(s), Some (ty) => ast::LitFloat(s, ty) }; MRExpr(cx.expr_lit(sp, lit)) } struct Ident { ident: ast::Ident, span: Span } fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) { let p = &mut parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.iter() .map(|x| (*x).clone()) .collect()); let ex = p.parse_expr(); let id = if p.token == token::EOF { None } else { p.expect(&token::COMMA); let lo = p.span.lo; let ident = p.parse_ident(); let hi = p.last_span.hi; Some(Ident{ident: ident, span: mk_sp(lo, hi)}) }; if p.token != token::EOF { p.unexpected(); } (ex, id) } // FIXME (10872): This is required to prevent an LLVM assert on Windows #[test] fn dummy_test() { } </ins>

<ins> // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // ignore-stage1 // ignore-pretty // ignore-cross-compile #12102 #[feature(phase)]; #[phase(syntax)] extern crate hexfloat; fn main() { hexfloat!("foo"); //~^ ERROR invalid hex float literal in hexfloat!: Expected '0' hexfloat!("0"); //~^ERROR invalid hex float literal in hexfloat!: Expected 'x' hexfloat!("0x"); //~^ERROR invalid hex float literal in hexfloat!: Expected '.' hexfloat!("0x."); //~^ERROR invalid hex float literal in hexfloat!: Expected digits before or after decimal point hexfloat!("0x0.0"); //~^ERROR invalid hex float literal in hexfloat!: Expected 'p' hexfloat!("0x0.0p"); //~^ERROR invalid hex float literal in hexfloat!: Expected exponent digits hexfloat!("0x0.0p0f"); //~^ERROR invalid hex float literal in hexfloat!: Expected end of string } </ins>

<ins> // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // ignore-stage1 // ignore-pretty // ignore-cross-compile #12102 #[feature(phase)]; #[phase(syntax)] extern crate hexfloat; fn main() { hexfloat!(foo); //~^ ERROR non-literal in hexfloat! hexfloat!(0); //~^ ERROR unsupported literal in hexfloat! hexfloat!("0x0.p0", invalid); //~^ ERROR invalid floating point type in hexfloat! } </ins>

<ins> // Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // ignore-stage1 // ignore-pretty // ignore-cross-compile #12102 // ignore-fast #[feature(phase)]; #[phase(syntax)] extern crate hexfloat; pub fn main() { let a = hexfloat!("0x1.999999999999ap-4"); assert_eq!(a, 0.1); let b = hexfloat!("-0x1.fffp-4", f32); assert_eq!(b, -0.12498474_f32); let c = hexfloat!("0x.12345p5", f64); let d = hexfloat!("0x0.12345p5", f64); assert_eq!(c,d); let f = hexfloat!("0x10.p4", f32); let g = hexfloat!("0x10.0p4", f32); assert_eq!(f,g); } </ins>