{"id":13730249,"url":"https://github.com/ulfjack/ryu","last_synced_at":"2025-04-08T08:15:38.528Z","repository":{"id":40593374,"uuid":"121561462","full_name":"ulfjack/ryu","owner":"ulfjack","description":"Converts floating point numbers to decimal strings","archived":false,"fork":false,"pushed_at":"2024-06-19T14:21:52.000Z","size":9008,"stargazers_count":1231,"open_issues_count":43,"forks_count":99,"subscribers_count":49,"default_branch":"master","last_synced_at":"2025-04-01T05:34:26.633Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ulfjack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-Apache2","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-14T21:07:38.000Z","updated_at":"2025-03-29T10:48:12.000Z","dependencies_parsed_at":"2024-11-12T23:42:14.163Z","dependency_job_id":"32332cfd-7c14-4d4a-b15f-2d7d61a81fc4","html_url":"https://github.com/ulfjack/ryu","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfjack%2Fryu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfjack%2Fryu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfjack%2Fryu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfjack%2Fryu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ulfjack","download_url":"https://codeload.github.com/ulfjack/ryu/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247801175,"owners_count":20998339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T02:01:12.197Z","updated_at":"2025-04-08T08:15:38.503Z","avatar_url":"https://github.com/ulfjack.png","language":"C++","readme":"# Ryu \u0026 Ryu Printf [![Build Status](https://travis-ci.org/ulfjack/ryu.svg?branch=master)](https://travis-ci.org/ulfjack/ryu)\n\nThis project contains routines to convert IEEE-754 floating-point numbers to\ndecimal strings using shortest, fixed `%f`, and scientific `%e`\nformatting. The primary implementation is in C, and there is a port of the\nshortest conversion to Java. All algorithms have been published in\npeer-reviewed publications. At the time of this writing, these are the fastest\nknown float-to-string conversion algorithms. The fixed, and scientific\nconversion routines are several times faster than the usual implementations\nof sprintf (we compared against glibc, Apple's libc, MSVC, and others).\n\nGenerating scientific and fixed output format for 16 and 32 bit IEEE floating\npoint numbers can be implemented by converting to 64 bit, and then using the\n64 bit routines. Note that there is no 128 bit implementation at this time.\n\nWhen converting to shortest, DO NOT CAST; shortest conversion is based on the\nprecision of the source type, and casting to a different type will not return\nthe expected output. There are highly optimized 32 and 64 bit implementations\nas well as a generic 128 bit implementation that can handle any IEEE format\nup to 128 bits.\n\nThese are the supported conversion modes for the C implementation:\n\n| IEEE Type            | Supported Output Formats         |\n| -------------------- | -------------------------------- |\n| 16 Bit (half)        | Shortest (via ryu_generic_128.h) |\n| 32 Bit (float)       | Shortest                         |\n| 64 Bit (double)      | Shortest, Scientific, Fixed      |\n| 80 Bit (long double) | Shortest (via ryu_generic_128.h) |\n| 128 Bit (__float128) | Shortest (via ryu_generic_128.h) |\n\nThe code is continuously tested on Ubuntu 18.04, MacOS High Sierra, and Windows\nServer version 1803.\n\nAll code outside of third_party/ is copyrighted by Ulf Adams and contributors,\nand may be used freely in accordance with the Apache 2.0 license.\nAlternatively, the files in the ryu/ directory may be used freely in accordance\nwith the Boost 1.0 license.\n\nAll contributions are required to maintain these licenses.\n\n## Ryu\nRyu generates the shortest decimal representation of a floating point number\nthat maintains round-trip safety. That is, a correct parser can recover the\nexact original number. For example, consider the binary 32-bit floating point\nnumber `00111110100110011001100110011010`. The stored value is exactly\n`0.300000011920928955078125`. However, this floating point number is also\nthe closest number to the decimal number `0.3`, so that is what Ryu\noutputs.\n\nThis problem of generating the shortest possible representation was originally\nposed by White and Steele [[1]], for which they described an algorithm called\n\"Dragon\". It was subsequently improved upon with algorithms that also had\ndragon-themed names. I followed in the same vein using the japanese word for\ndragon, Ryu. In general, all these algorithms should produce identical output\ngiven identical input, and this is checked when running the benchmark program.\n\nThe C implementation of Ryu is in the ryu/ directory. The Java implementations\nare RyuFloat and RyuDouble under src/main/java/. Both cover 32 and 64-bit\nfloating point numbers.\n\nIn addition, there is an experimental C implementation that can handle inputs\nof any size up to 128-bit, albeit with lower performance than the highly\noptimized 32-bit and 64-bit implementations. Furthermore, there is an\nexperimental low-level C API that returns the decimal floating-point\nrepresentation as a struct, allowing clients to implement their own formatting.\nThese are still subject to change.\n\n*Note*: The Java implementation differs from the output of `Double.toString`\n[[2]] in some cases: sometimes the output is shorter (which is arguably more\naccurate) and sometimes the output may differ in the precise digits output\n(e.g., see https://github.com/ulfjack/ryu/issues/83).\n\n*Note*: While the Java specification requires outputting at least 2 digits,\nother specifications, such as for JavaScript, always require the shortest output.\nWe may change the Java implementation in the future to support both.\n\nMy PLDI'18 paper includes a complete correctness proof of the algorithm:\nhttps://dl.acm.org/citation.cfm?doid=3296979.3192369\n\nOther implementations of Ryu:\n\n| Language         | Author             | Link                                          |\n|------------------|--------------------|-----------------------------------------------|\n| Scala            | Andriy Plokhotnyuk | [https://github.com/plokhotnyuk/jsoniter-scala][3] |\n| Rust             | David Tolnay       | https://github.com/dtolnay/ryu                |\n| Julia            | Jacob Quinn        | https://github.com/JuliaLang/julia/tree/master/base/ryu |\n| Factor           | Alexander Iljin    | https://github.com/AlexIljin/ryu              |\n| Go               | Caleb Spare        | https://github.com/cespare/ryu                |\n| C#               | Dogwei             | https://github.com/Dogwei/RyuCsharp           |\n| C#               | Shad Storhaug      | https://github.com/NightOwl888/J2N            |\n| D                | Ilya Yaroshenko    | [https://github.com/libmir/mir-algorithm][5]  |\n| Scala            | Denys Shabalin     | [https://github.com/scala-native/scala-native][4] |\n| Erlang/BEAM      | Thomas Depierre    | https://github.com/erlang/otp/tree/master/erts/emulator/ryu |\n| Zig              | Marc Tiehuis       | https://github.com/tiehuis/zig-ryu            |\n| Haskell          | [Lawrence Wu](https://github.com/la-wu) | [https://github.com/haskell/bytestring][6] |\n\n[1]: https://dl.acm.org/citation.cfm?id=93559\n\n[2]: https://docs.oracle.com/javase/10/docs/api/java/lang/Double.html#toString(double)\n\n[3]: https://github.com/plokhotnyuk/jsoniter-scala/blob/6e6bb9d7bed6de341ce0b781b403eb671d008468/jsoniter-scala-core/jvm/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonWriter.scala#L1777-L2262\n\n[4]: https://github.com/scala-native/scala-native/tree/master/nativelib/src/main/scala/scala/scalanative/runtime/ieee754tostring/ryu\n\n[5]: https://github.com/libmir/mir-algorithm/tree/master/source/mir/bignum/internal/ryu\n\n[6]: https://github.com/haskell/bytestring/pull/365\n\n## Ryu Printf\nSince Ryu generates the shortest decimal representation, it is not immediately\nsuitable for use in languages that have printf-like facilities. In most\nimplementations, printf provides three floating-point specific formatters,\n`%f`, `%e`, and `%g`:\n\n - The `%f` format prints the full decimal part of the given floating point\n   number, and then appends as many digits of the fractional part as specified\n   using the precision parameter.\n\n - The `%e` format prints the decimal number in scientific notation with as\n   many digits after the initial digit as specified using the precision\n   parameter.\n\n - The `%g` format prints either `%f` or `%e` format, whichever is\n   shorter.\n\nRyu Printf implements %f and %e formatting in a way that should be drop-in\ncompatible with most implementations of printf, although it currently does not\nimplement any formatting flags other than precision. The benchmark program\nverifies that the output matches exactly, and outputs a warning if not. Any\nunexpected output from the benchmark indicates a difference in output.\n\n*Note* that old versions of MSVC ship with a printf implementation that has a\nconfirmed bug: it does not always round the last digit correctly.\n\n*Note* that msys cuts off the output after ~17 digits, and therefore generally\ndiffers from Ryu Printf output for precision values larger than 17.\n\n*Note* that the output for NaN values can differ between implementations; we use\nifdefs in an attempt to match platform output.\n\nAccording to our benchmarks, Ryu Printf compares favorably with the following\nimplementations of printf for precision parameters 1, 10, 100, and 1000:\n\n| OS                   | Libc                        | Ryu Printf is faster by |\n|----------------------|-----------------------------|-------------------------|\n| Ubuntu 18.04         | libc6 2.27-3ubuntu1         | 15x                     |\n| Ubuntu 18.04         | musl 1.1.19-1               | 4x                      |\n| Windows 10 Home 1803 | MSVC 19.14.26429.4          | 9x                      |\n| Windows 10 Home 1803 | msys-runtime-devel 2.10.0-2 | between 8x and 20x      |\n| macOS Mojave 10.14   | Apple Libc                  | 24x                     |\n\nIn addition, Ryu Printf has a more predictable performance profile. In theory,\nan implementation that performs particularly badly for some subset of numbers\ncould be exploited as a denial-of-service attack vector.\n\nMy OOPSLA'2019 paper provides a correctness proof:\nhttps://dl.acm.org/citation.cfm?doid=3366395.3360595\n\n\n\n## Building, Testing, Running\n\nWe use the Bazel build system (https://bazel.build) 0.14 or later, although we\nrecommend using the latest release. You also need to install Jdk 8 (or later)\nto build and run the Java code, and/or a C/C++ compiler (gcc or clang on Ubuntu,\nXCode on MacOS, or MSVC on Windows) to build the C/C++ code.\n\nTo build Ryu, run\n```\n$ bazel build //ryu\n```\n\nTo build Ryu Printf, run\n```\n$ bazel build //ryu:ryu_printf\n```\n\n### Big-Endian Architectures\nThe C implementations should work on big-endian architectures provided that the\nfloating point type and the corresponding integer type use the same endianness.\n\nThere are no concerns around endianness for the Java implementation.\n\n### Building with a Custom Compiler\nYou can select a custom C++ compiler by setting the CC environment variable,\ne.g., use these steps to build with clang-4.0 on Ubuntu:\n```\n$ export CC=clang-4.0\n$ bazel build //ryu\n```\n\nBuilding Ryu Printf against musl and msys requires installing the corresponding\npackages. We only tested against the musl Debian package that installs a gcc\nwrapper and is enabled by setting `CC`. However, building against msys\nrequires manually adjusting Bazel's compiler configuration files.\n\n### Tests\nYou can run both C and Java tests with\n```\n$ bazel test //ryu/... //src/...\n```\n\n\n\n## Ryu: Additional Notes\n\n### Jaffer\nThe code given by Jaffer in the original paper does not come with a license\ndeclaration. Instead, we're using code found on GitHub [3], which contains a\nlicense declaration by Jaffer. Compared to the original code, this\nimplementation no longer outputs incorrect values for negative numbers.\n\nWe provide a binary to find differences between Ryu and the Jaffer / Jdk\nimplementations:\n```\n$ bazel run //src/main/java/info/adams/ryu/analysis:FindDifferences --\n```\n\nAdd the `-mode=csv` option to get all the discovered differences as a CSV. Use\n`-mode=latex` instead to get a latex snippet of the first 20. Use\n`-mode=summary` to only print the number of discovered differences (this is the\ndefault mode).\n\n[3]: https://github.com/coconut2015/cookjson/blob/master/cookjson-core/src/main/java/org/yuanheng/cookjson/DoubleUtils.java\n\n### Computing Required Lookup Table Sizes\nYou can compute the required lookup table sizes with:\n```\n$ bazel run //src/main/java/info/adams/ryu/analysis:ComputeTableSizes --\n```\n\nAdd `-v` to get slightly more verbose output.\n\n### Computing Required Bit Sizes\nYou can compute the required bit sizes with:\n```\n$ bazel run //src/main/java/info/adams/ryu/analysis:ComputeRequiredBitSizes --\n```\n\nAdd the `-128` and `-256` flags to also cover 128- and 256-bit numbers. This\ncould take a while - 128-bit takes ~20 seconds on my machine while 256-bit takes\na few hours. Add `-v` to get very verbose output.\n\n### Java: Comparing All Possible 32-bit Values Exhaustively\nYou can check the slow vs. the fast implementation for all 32-bit floating point\nnumbers using:\n```\n$ bazel run //src/main/java/info/adams/ryu/analysis:ExhaustiveFloatComparison\n```\n\nThis takes ~60 hours to run to completion on an\nIntel(R) Core(TM) i7-4770K with 3.50GHz.\n\n### Java: Comparing All Possible 64-bit Values Exhaustively\nYou can check the slow vs. the fast implementation for all 64-bit floating point\nnumbers using:\n```\n$ bazel run //src/main/java/info/adams/ryu/analysis:ExtensiveDoubleComparison\n```\n\nThis takes approximately forever, so you will need to interrupt the program.\n\n\n\n## Benchmarks\n\n### Ryu\nWe provide both C and Java benchmark programs.\n\nEnable optimization by adding \"-c opt\" on the command line:\n```\n$ bazel run -c opt //ryu/benchmark:ryu_benchmark --\n    Average \u0026 Stddev Ryu  Average \u0026 Stddev Grisu3\n32:   22.515    1.578       90.981   41.455\n64:   27.545    1.677       98.981   80.797\n```\n\nFor the Java benchmark, run:\n```\n$ bazel run //src/main/java/info/adams/ryu/benchmark --\n    Average \u0026 Stddev Ryu  Average \u0026 Stddev Jdk  Average \u0026 Stddev Jaffer\n32:   56.680    9.127       254.903  170.099\n64:   89.751   13.442      1085.596  302.371     1089.535  309.245\n```\n\nAdditional parameters can be passed to the benchmark after the `--` parameter:\n```\n  -32           only run the 32-bit benchmark\n  -64           only run the 64-bit benchmark\n  -samples=n    run n pseudo-randomly selected numbers\n  -iterations=n run each number n times\n  -ryu          run Ryu only, no comparison\n  -v            generate verbose output in CSV format\n```\n\nIf you have gnuplot installed, you can generate plots from the benchmark data\nwith:\n```\n$ bazel build -c opt --jobs=1 //scripts:shortest-{c,java}-{float,double}.pdf\n```\n\nThe resulting files are `bazel-genfiles/scripts/shortest-{c,java}-{float,double}.pdf`.\n\n### Ryu Printf\nWe provide a C++ benchmark program that runs against the implementation of\n`snprintf` bundled with the selected C++ compiler. You need to enable\noptimization using \"-c opt\" on the command line:\n```\n$ bazel run -c opt //ryu/benchmark:ryu_printf_benchmark --\n    Average \u0026 Stddev Ryu  Average \u0026 Stddev snprintf\n%f:  116.359  130.992     3983.251 5331.505\n%e:   40.853   10.872      210.648   36.779\n```\n\nAdditional parameters can be passed to the benchmark after the `--` parameter:\n```\n  -f            only run the %f benchmark\n  -e            only run the %e benchmark\n  -precision=n  run with precision n (default is 6)\n  -samples=n    run n pseudo-randomly selected numbers\n  -iterations=n run each number n times\n  -ryu          run Ryu Printf only, no comparison\n  -v            generate verbose output in CSV format\n```\n\nSee above for selecting a different compiler. Note that msys C++ compilation\ndoes not work out of the box.\n\nWe also provide a simplified C benchmark for platforms that do not support C++\ncompilation, but *note* that pure C compilation is not natively supported by\nBazel:\n```\n$ bazel run -c opt //ryu/benchmark:ryu_printf_benchmark_c --\n```\n\nIf you have gnuplot installed, you can generate plots from the benchmark data\nwith:\n```\n$ bazel build -c opt --jobs=1 //scripts:{f,e}-c-double-{1,10,100,1000}.pdf\n```\n\nThe resulting files are `bazel-genfiles/scripts/{f,e}-c-double-{1,10,100,1000}.pdf`.\n","funding_links":[],"categories":["C++"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fulfjack%2Fryu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fulfjack%2Fryu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fulfjack%2Fryu/lists"}