mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 04:23:36 +01:00
kprint: add floating-point formatting based in Grisu2b 59,56
This commit is contained in:
parent
023675d449
commit
bbf6401213
15 changed files with 773 additions and 30 deletions
|
@ -31,6 +31,7 @@ set(SOURCES_COMMON
|
|||
src/keysc/keycodes.c
|
||||
src/keysc/keysc.c
|
||||
src/kprint/kprint.c
|
||||
src/kprint/kformat_fp.c
|
||||
src/mmu/mmu.c
|
||||
src/render/dhline.c
|
||||
src/render/dimage.c
|
||||
|
@ -56,6 +57,7 @@ set(SOURCES_COMMON
|
|||
src/tmu/tmu.c
|
||||
src/3rdparty/tinymt32/rand.c
|
||||
src/3rdparty/tinymt32/tinymt32.c
|
||||
src/3rdparty/grisu2b_59_56/grisu2b_59_56.c
|
||||
)
|
||||
set(SOURCES_FX
|
||||
src/gray/engine.c
|
||||
|
@ -104,9 +106,16 @@ set(ASSETS_FX src/font5x7.png)
|
|||
set(ASSETS_CG src/font8x9.png)
|
||||
fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG})
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include")
|
||||
include_directories(
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"${FXSDK_COMPILER_INSTALL}/include/openlibm")
|
||||
add_compile_options(-Wall -Wextra -std=c11 -Os -fstrict-volatile-bitfields)
|
||||
|
||||
# Silence extended warnings on Grisu2b code
|
||||
set_source_files_properties(src/3rdparty/grisu2b_59_56/grisu2b_59_56.c PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-all;-Wno-extra")
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
add_compile_definitions(FX9860G)
|
||||
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX})
|
||||
|
|
|
@ -13,11 +13,15 @@ This is free software: you may use it for any purpose, share it, modify it, and
|
|||
share your changes. Credit is not required, but please let me know!
|
||||
|
||||
gint also includes third-party code that is distributed under its own license.
|
||||
Currently, this only includes:
|
||||
Currently, this includes:
|
||||
|
||||
* A stripped-down version of the [TinyMT random number generator](http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/TINYMT/index.html)
|
||||
([GitHub repository](https://github.com/MersenneTwister-Lab/TinyMT)) by
|
||||
Mutsuo Saito and Makoto Matsumoto. See `src/3rdparty/tinymt32/LICENSE.txt`.
|
||||
* A stripped-down version of the [Grisu2b floating-point representation
|
||||
algorithm](https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
|
||||
with α=-59 and γ=-56, by Florian Loitsch. See `src/3rdparty/grisu2b_59_56/README`
|
||||
for details [the original code here](https://drive.google.com/open?id=0BwvYOx00EwKmejFIMjRORTFLcTA).
|
||||
|
||||
## Programming interface
|
||||
|
||||
|
|
|
@ -19,6 +19,13 @@
|
|||
see <stdio.h>. */
|
||||
size_t kvsprint(char *output, size_t len, char const *format, va_list *args);
|
||||
|
||||
/* kprint_enable_fp(): Enable and load floating-point formats
|
||||
Loads floating-point formats %e, %E, %f, %F, %g and %G. By default these
|
||||
formats are not enabled because the formatting code takes a large amount of
|
||||
space. Calling this function pulls the floating-point formatter from the
|
||||
gint library at link time. */
|
||||
void kprint_enable_fp(void);
|
||||
|
||||
//---
|
||||
// Printer extensions
|
||||
//---
|
||||
|
@ -34,7 +41,7 @@ typedef struct {
|
|||
/* Minimal length of formatted string (padding can be added) */
|
||||
uint16_t length;
|
||||
/* How much significant characters of data, meaning varies */
|
||||
uint16_t precision;
|
||||
int16_t precision;
|
||||
|
||||
/* Size specifier for integers (%o, %x, %i, %d, %u), may be one of:
|
||||
(0) char (8-bit)
|
||||
|
@ -78,6 +85,15 @@ typedef void (*kprint_formatter_t)(KFORMAT_ARGS);
|
|||
@kformat Format function */
|
||||
void kprint_register(int spec, kprint_formatter_t kformat);
|
||||
|
||||
/* kprint_out(): Output a single character to the kprint buffer */
|
||||
void kprint_out(int byte);
|
||||
|
||||
/* kprint_outn(): Output the same character <n> times */
|
||||
void kprint_outn(int byte, int n);
|
||||
|
||||
/* kprint_outstr(): Output a string to the kprint buffer */
|
||||
void kprint_outstr(char const *str, int n);
|
||||
|
||||
//---
|
||||
// Helper functions for formatters
|
||||
//---
|
||||
|
@ -103,8 +119,8 @@ typedef struct
|
|||
/* Style of display:
|
||||
KPRINT_GENERIC Sign ignored, 0-padding ignored
|
||||
KPRINT_INTEGER .precision causes 0-padding
|
||||
KPRINT_NUMERICAL No effect */
|
||||
enum { KPRINT_GENERIC = 0, KPRINT_INTEGER, KPRINT_NUMERICAL } style;
|
||||
KPRINT_NUMERIC No effect */
|
||||
enum { KPRINT_GENERIC = 0, KPRINT_INTEGER, KPRINT_NUMERIC } style;
|
||||
|
||||
} GPACKED(2) kprint_geometry_t;
|
||||
|
||||
|
@ -115,10 +131,11 @@ typedef struct
|
|||
* g->prefix, the length of the desired prefix (if unused, 0)
|
||||
* g->content, the natural content length for the provided data
|
||||
* g->sign, the sign of the input ("+" or "-"); for KPRINT_GENERIC, 0
|
||||
* g->style, which affects the meaning of options
|
||||
|
||||
This function outputs:
|
||||
* g->sign, which can be changed to " " or NUL depending on opt
|
||||
* All other fields of g that are not part of the input
|
||||
* g->sign, which will be changed to " " or NUL (0) depending on options
|
||||
* All fields of g that are not part of the input
|
||||
|
||||
The algorithm for laying out the format is as follows.
|
||||
1. For numerical and integer formats, turn a "+" sign into " " if
|
||||
|
|
|
@ -10,20 +10,28 @@
|
|||
|
||||
/* Formatted printing functions
|
||||
|
||||
These functions implement most of printf()'s features, including:
|
||||
These functions implement most of printf(3)'s features, including:
|
||||
* Signed and unsigned integer formats (%d, %i, %o, %u, %x, %X)
|
||||
* Character, string and pointer formats (%c, %s, %p)
|
||||
* Format options (0, #, -, (space), length, precision)
|
||||
* Parameter size (hh, h, l, ll)
|
||||
* Parameter length (hh, h, l, ll, z)
|
||||
* Limiting the size of the output and still returning the whole length
|
||||
* If kprint_enable_fp() from <gint/kprint.h> is called: floating-point
|
||||
formats (%e, %E, %f, %F, %g, %G) (disabled by default for space)
|
||||
|
||||
They do not support:
|
||||
* Floating-point (%e, %E, %f, %F, %g, %G, %a, %A)
|
||||
* Exotic integer types (intmax_t) or features (thousands separators)
|
||||
* Hexadecimal floating-point (%a, %A)
|
||||
* The strerror() shorthand, since there is no errno (%m)
|
||||
* Some size modifiers: long double (L), intmax_t (j), ptrdiff_t (t), and the
|
||||
nonstandard synonyms q (ll) and Z (z)
|
||||
* Dynamic length field (*)
|
||||
* Thousands separators (') and locale-aware digits (I)
|
||||
* Nonstandard synonyms %C (%lc) and %S (%ls)
|
||||
|
||||
A new fixed-point format %j has been added; it behaves like %d but includes
|
||||
a decimal point. The number of decimal places is specified by the precision
|
||||
field. */
|
||||
field. For instance %.3j with 12345 prints "123.45". This can be used for
|
||||
decimal fixed-point values. */
|
||||
|
||||
/* Print to string from var args */
|
||||
int sprintf(char *str, char const *format, ...);
|
||||
|
|
22
src/3rdparty/grisu2b_59_56/LICENSE
vendored
Normal file
22
src/3rdparty/grisu2b_59_56/LICENSE
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2009 Florian Loitsch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
14
src/3rdparty/grisu2b_59_56/README
vendored
Normal file
14
src/3rdparty/grisu2b_59_56/README
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
Grisu is an algorithm by Florian Loitsch to print the decimal representation of
|
||||
floating-point numbers.
|
||||
|
||||
The original code from which this folder is extracted can be found at
|
||||
<https://drive.google.com/open?id=0BwvYOx00EwKmejFIMjRORTFLcTA>.
|
||||
|
||||
Only a minimal variation, the Grisu2 rounding algorithm with α=-59 and γ=-56,
|
||||
is present here. The code has been adapted to fit the limited runtime as well
|
||||
as the use of OpenLibm.
|
||||
|
||||
See the original paper at <https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf>
|
||||
and Florian Loitsch's home page at <https://florian.loitsch.com/publications>.
|
||||
|
||||
The files are licensed under the permissive conditions of the attached LICENSE.
|
58
src/3rdparty/grisu2b_59_56/diy_fp.h
vendored
Normal file
58
src/3rdparty/grisu2b_59_56/diy_fp.h
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright (c) 2009 Florian Loitsch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct diy_fp_t {
|
||||
uint64_t f;
|
||||
int e;
|
||||
} diy_fp_t;
|
||||
|
||||
static diy_fp_t minus(diy_fp_t x, diy_fp_t y) {
|
||||
diy_fp_t r = {.f = x.f - y.f, .e = x.e};
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
static diy_fp_t minus(diy_fp_t x, diy_fp_t y) {
|
||||
assert(x.e == y.e);
|
||||
assert(x.f >= y.f);
|
||||
diy_fp_t r = {.f = x.f - y.f, .e = x.e};
|
||||
return r;
|
||||
}
|
||||
*/
|
||||
|
||||
static diy_fp_t multiply(diy_fp_t x, diy_fp_t y) {
|
||||
uint64_t a,b,c,d,ac,bc,ad,bd,tmp,h;
|
||||
diy_fp_t r; uint64_t M32 = 0xFFFFFFFF;
|
||||
a = x.f >> 32; b = x.f & M32;
|
||||
c = y.f >> 32; d = y.f & M32;
|
||||
ac = a*c; bc = b*c; ad = a*d; bd = b*d;
|
||||
tmp = (bd>>32) + (ad&M32) + (bc&M32);
|
||||
tmp += 1U << 31; /// mult_round
|
||||
r.f = ac+(ad>>32)+(bc>>32)+(tmp >>32);
|
||||
r.e = x.e + y.e + 64;
|
||||
return r;
|
||||
}
|
110
src/3rdparty/grisu2b_59_56/double.h
vendored
Normal file
110
src/3rdparty/grisu2b_59_56/double.h
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright (c) 2009 Florian Loitsch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "diy_fp.h"
|
||||
#include "powers.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef union {
|
||||
double d;
|
||||
uint64_t n;
|
||||
} converter_t;
|
||||
|
||||
static uint64_t double_to_uint64(double d) { converter_t tmp; tmp.d = d; return tmp.n; }
|
||||
static double uint64_to_double(uint64_t d64) { converter_t tmp; tmp.n = d64; return tmp.d; }
|
||||
|
||||
#define DP_SIGNIFICAND_SIZE 52
|
||||
#define DP_EXPONENT_BIAS (0x3FF + DP_SIGNIFICAND_SIZE)
|
||||
#define DP_MIN_EXPONENT (-DP_EXPONENT_BIAS)
|
||||
#define DP_EXPONENT_MASK 0x7FF0000000000000
|
||||
#define DP_SIGNIFICAND_MASK 0x000FFFFFFFFFFFFF
|
||||
#define DP_HIDDEN_BIT 0x0010000000000000
|
||||
|
||||
static diy_fp_t normalize_diy_fp(diy_fp_t in) {
|
||||
diy_fp_t res = in;
|
||||
/* Normalize now */
|
||||
/* the original number could have been a denormal. */
|
||||
while (! (res.f & DP_HIDDEN_BIT))
|
||||
{
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
/* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
|
||||
res.f <<= (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 1);
|
||||
res.e = res.e - (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
static diy_fp_t double2diy_fp(double d) {
|
||||
uint64_t d64 = double_to_uint64(d);
|
||||
int biased_e = (d64 & DP_EXPONENT_MASK) >> DP_SIGNIFICAND_SIZE;
|
||||
uint64_t significand = (d64 & DP_SIGNIFICAND_MASK);
|
||||
diy_fp_t res;
|
||||
if (biased_e != 0)
|
||||
{
|
||||
res.f = significand + DP_HIDDEN_BIT;
|
||||
res.e = biased_e - DP_EXPONENT_BIAS;
|
||||
} else {
|
||||
res.f = significand;
|
||||
res.e = DP_MIN_EXPONENT + 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static diy_fp_t normalize_boundary(diy_fp_t in) {
|
||||
diy_fp_t res = in;
|
||||
/* Normalize now */
|
||||
/* the original number could have been a denormal. */
|
||||
while (! (res.f & (DP_HIDDEN_BIT << 1)))
|
||||
{
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
/* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
|
||||
res.f <<= (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
|
||||
res.e = res.e - (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void normalized_boundaries(double d, diy_fp_t* out_m_minus, diy_fp_t* out_m_plus) {
|
||||
diy_fp_t v = double2diy_fp(d);
|
||||
diy_fp_t pl, mi;
|
||||
bool significand_is_zero = v.f == DP_HIDDEN_BIT;
|
||||
pl.f = (v.f << 1) + 1; pl.e = v.e - 1;
|
||||
pl = normalize_boundary(pl);
|
||||
if (significand_is_zero)
|
||||
{
|
||||
mi.f = (v.f << 2) - 1;
|
||||
mi.e = v.e - 2;
|
||||
} else {
|
||||
mi.f = (v.f << 1) - 1;
|
||||
mi.e = v.e - 1;
|
||||
}
|
||||
mi.f <<= mi.e - pl.e;
|
||||
mi.e = pl.e;
|
||||
*out_m_plus = pl;
|
||||
*out_m_minus = mi;
|
||||
}
|
27
src/3rdparty/grisu2b_59_56/grisu2.h
vendored
Normal file
27
src/3rdparty/grisu2b_59_56/grisu2.h
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright (c) 2009 Florian Loitsch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void grisu2(double v, char* buffer, int* length, int* K);
|
95
src/3rdparty/grisu2b_59_56/grisu2b_59_56.c
vendored
Normal file
95
src/3rdparty/grisu2b_59_56/grisu2b_59_56.c
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright (c) 2009 Florian Loitsch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "diy_fp.h"
|
||||
#include "k_comp.h"
|
||||
#include "double.h"
|
||||
#include "powers.h"
|
||||
#include <stdbool.h>
|
||||
#include "grisu2.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define TEN9 1000000000
|
||||
|
||||
void grisu_round(char* buffer, int len,
|
||||
uint64_t delta, uint64_t rest,
|
||||
uint64_t ten_kappa, uint64_t wp_w) {
|
||||
while (rest < wp_w &&
|
||||
delta - rest >= ten_kappa &&
|
||||
(rest + ten_kappa < wp_w || /// closer
|
||||
wp_w - rest > rest+ten_kappa - wp_w))
|
||||
{
|
||||
buffer[len-1]--; rest += ten_kappa;
|
||||
}
|
||||
}
|
||||
|
||||
void digit_gen(diy_fp_t W, diy_fp_t Mp, diy_fp_t delta,
|
||||
char* buffer, int* len, int* K) {
|
||||
uint32_t div; int d,kappa; diy_fp_t one, wp_w;
|
||||
wp_w = minus(Mp, W);
|
||||
one.f = ((uint64_t) 1) << -Mp.e; one.e = Mp.e;
|
||||
uint32_t p1 = Mp.f >> -one.e; /// Mp_cut
|
||||
uint64_t p2 = Mp.f & (one.f - 1);
|
||||
*len = 0; kappa = 10; div = TEN9;
|
||||
while (kappa > 0) {
|
||||
d = p1 / div;
|
||||
if (d || *len) buffer[(*len)++] = '0' + d; /// Mp_inv1
|
||||
p1 %= div; kappa--;
|
||||
uint64_t tmp = (((uint64_t)p1)<<-one.e)+p2;
|
||||
if (tmp <= delta.f) { /// Mp_delta
|
||||
*K += kappa;
|
||||
grisu_round(buffer, *len, delta.f, tmp, ((uint64_t)div) << -one.e, wp_w.f);
|
||||
return;
|
||||
}
|
||||
div /= 10;
|
||||
}
|
||||
uint64_t unit = 1;
|
||||
while (1) {
|
||||
p2 *= 10; delta.f *= 10; unit *= 10;
|
||||
d = p2 >> -one.e;
|
||||
if (d || *len) buffer[(*len)++] = '0' + d;
|
||||
p2 &= one.f - 1; kappa--;
|
||||
if (p2 < delta.f) {
|
||||
*K += kappa;
|
||||
grisu_round(buffer, *len, delta.f, p2, one.f, wp_w.f*unit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void grisu2(double v, char* buffer, int* length, int* K) {
|
||||
diy_fp_t w_m, w_p;
|
||||
int q = 64, alpha = -59, gamma = -56; int pos;
|
||||
normalized_boundaries(v, &w_m, &w_p);
|
||||
diy_fp_t w = normalize_diy_fp(double2diy_fp(v));
|
||||
int mk = k_comp(w_p.e + q, alpha, gamma);
|
||||
diy_fp_t c_mk = cached_power(mk);
|
||||
diy_fp_t W = multiply(w, c_mk);
|
||||
diy_fp_t Wp = multiply(w_p, c_mk);
|
||||
diy_fp_t Wm = multiply(w_m, c_mk);
|
||||
Wm.f++; Wp.f--;
|
||||
diy_fp_t delta = minus(Wp, Wm);
|
||||
*K = -mk;
|
||||
digit_gen(W, Wp, delta, buffer, length, K);
|
||||
}
|
32
src/3rdparty/grisu2b_59_56/k_comp.h
vendored
Normal file
32
src/3rdparty/grisu2b_59_56/k_comp.h
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright (c) 2009 Florian Loitsch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <openlibm.h>
|
||||
|
||||
#define D_1_LOG2_10 0.30102999566398114 // 1 / lg(10)
|
||||
|
||||
static int k_comp(int e, int alpha, int gamma) {
|
||||
return ceil((alpha-e+63) * D_1_LOG2_10);
|
||||
}
|
27
src/3rdparty/grisu2b_59_56/powers.h
vendored
Normal file
27
src/3rdparty/grisu2b_59_56/powers.h
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright (c) 2009 Florian Loitsch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include "diy_fp.h"
|
||||
#include "powers_ten_round64.h"
|
36
src/3rdparty/grisu2b_59_56/powers_ten_round64.h
vendored
Normal file
36
src/3rdparty/grisu2b_59_56/powers_ten_round64.h
vendored
Normal file
File diff suppressed because one or more lines are too long
281
src/kprint/kformat_fp.c
Normal file
281
src/kprint/kformat_fp.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
//---
|
||||
// gint:kprint:kformat-fp: Floating-point formatted printing
|
||||
//
|
||||
// This object file provides floating-point formatters. It can be linked in by
|
||||
// calling kprint_enable_fp() and will automatically register floating-point
|
||||
// formatters that use the Grisu2b algorithm.
|
||||
//---
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/kprint.h>
|
||||
|
||||
#include "../3rdparty/grisu2b_59_56/grisu2.h"
|
||||
|
||||
//---
|
||||
// String generation for doubles
|
||||
//---
|
||||
|
||||
/* The grisu2() function does not have a size limit so we generate into a
|
||||
buffer large enough to hold the result. */
|
||||
static char digit_buffer[50];
|
||||
|
||||
/* Round a number at the specified place (which can be out of bounds). An extra
|
||||
byte before the buffer should be secured to leave room for a new digit in
|
||||
case a carry reaches there. Returns the new start-of-buffer.
|
||||
|
||||
@buffer Buffer with generated digits, updated if needed
|
||||
@length Number of digits generated in the buffer, updated if needed
|
||||
@e Location of decimal dot relative to integer in buffer
|
||||
@place Decimal to place to round to */
|
||||
static void round_str(char **buffer_ptr, int *length, int e, int place)
|
||||
{
|
||||
char *buffer = *buffer_ptr;
|
||||
|
||||
/* Interpret place as relative to buffer indices */
|
||||
place += *length + e - 1;
|
||||
|
||||
/* Specified place is out-of-bounds */
|
||||
if(place < 0 || place >= *length - 1) return;
|
||||
/* Next digit is 0..4 so rounding has no effect */
|
||||
if(buffer[place + 1] < '5') return;
|
||||
|
||||
/* Propagate carries if needed */
|
||||
while(place >= -1)
|
||||
{
|
||||
buffer[place]++;
|
||||
if(buffer[place] <= '9') break;
|
||||
|
||||
buffer[place] = '0';
|
||||
place--;
|
||||
}
|
||||
|
||||
/* Add one digit if needed */
|
||||
if(place >= 0) return;
|
||||
(*buffer_ptr)--;
|
||||
(*length)++;
|
||||
}
|
||||
|
||||
/* Remove zeros at the end of the digits, reducing [length] accordingly. */
|
||||
static int remove_zeros(char *buffer, int length, int unremovable)
|
||||
{
|
||||
int removed = 0;
|
||||
|
||||
while(length > unremovable && buffer[length - 1] == '0')
|
||||
{
|
||||
buffer[length - 1] = 0;
|
||||
length--;
|
||||
removed++;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/* Handles infinities and NaNs. */
|
||||
static int special_notation(double v, int upper)
|
||||
{
|
||||
if(__builtin_isinf(v) && v < 0)
|
||||
{
|
||||
kprint_outstr(upper ? "-INF" : "-inf", 4);
|
||||
return 1;
|
||||
}
|
||||
if(__builtin_isinf(v))
|
||||
{
|
||||
kprint_outstr(upper ? "INF" : "inf", 3);
|
||||
return 1;
|
||||
}
|
||||
if(__builtin_isnan(v))
|
||||
{
|
||||
kprint_outstr(upper ? "NAN" : "nan", 3);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prints decimal explicitly for %f and %g. */
|
||||
static void direct_notation(kprint_options_t *opt, kprint_geometry_t g,
|
||||
char *digits, int length, int e)
|
||||
{
|
||||
/* Number of characters for decimal part, including dot */
|
||||
int dec_chars = opt->precision + (opt->precision > 0);
|
||||
|
||||
/* See case discussion below */
|
||||
g.content = (length + e >= 0) ? length + e + dec_chars : 1 + dec_chars;
|
||||
kformat_geometry(opt, &g);
|
||||
|
||||
kprint_outn(' ', g.left_spaces);
|
||||
if(g.sign) kprint_out(g.sign);
|
||||
kprint_outn('0', g.zeros);
|
||||
|
||||
int pre = opt->precision;
|
||||
|
||||
if(e >= 0) /* xxxxxx00[.00] */
|
||||
{
|
||||
/* Decimal dot is after digits; rounding never occurs */
|
||||
kprint_outstr(digits, length);
|
||||
kprint_outn('0', e);
|
||||
|
||||
if(pre > 0)
|
||||
{
|
||||
kprint_out('.');
|
||||
kprint_outn('0', pre);
|
||||
}
|
||||
}
|
||||
else if(length + e > 0) /* xxxy(.xx), xx.xy(xx), xx.xxxx[00] */
|
||||
{
|
||||
/* Decimal dot is within the digits; we might have rounded */
|
||||
kprint_outstr(digits, length + e);
|
||||
|
||||
if(pre > 0)
|
||||
{
|
||||
kprint_out('.');
|
||||
kprint_outstr(digits + length + e, min(-e, pre));
|
||||
kprint_outn('0', pre + e);
|
||||
}
|
||||
}
|
||||
else if(length + e <= 0) /* 0.00(00xxxx), 0.00xy(xx), 0.00xxxx00 */
|
||||
{
|
||||
/* Decimal dot is before the digits; we might have rounded */
|
||||
kprint_out('0');
|
||||
if(pre > 0)
|
||||
{
|
||||
kprint_out('.');
|
||||
kprint_outn('0', min(-e - length, pre));
|
||||
kprint_outstr(digits, min(length, pre + length + e));
|
||||
kprint_outn('0', pre + e);
|
||||
}
|
||||
}
|
||||
|
||||
kprint_outn(' ', g.right_spaces);
|
||||
}
|
||||
|
||||
/* Prints exponent notation for %e and %g. */
|
||||
static void exponent_notation(kprint_options_t *opt, kprint_geometry_t g,
|
||||
char *digits, int length, int e, int uppercase)
|
||||
{
|
||||
int true_e = e + length - 1;
|
||||
|
||||
/* Number of characters for decimal part and for exponent */
|
||||
int dec_chars = opt->precision + (opt->precision > 0);
|
||||
int exp_chars = 4 + (true_e >= 100 || true_e <= -100);
|
||||
|
||||
g.content = 1 + dec_chars + exp_chars;
|
||||
kformat_geometry(opt, &g);
|
||||
|
||||
kprint_outn(' ', g.left_spaces);
|
||||
if(g.sign) kprint_out(g.sign);
|
||||
kprint_outn('0', g.zeros);
|
||||
|
||||
/* Digits */
|
||||
kprint_out(digits[0]);
|
||||
if(opt->precision > 0)
|
||||
{
|
||||
kprint_out('.');
|
||||
kprint_outstr(digits + 1, min(length - 1, opt->precision));
|
||||
kprint_outn('0', opt->precision - (length - 1));
|
||||
}
|
||||
|
||||
/* Exponent */
|
||||
kprint_out(uppercase ? 'E' : 'e');
|
||||
kprint_out(true_e >= 0 ? '+' : '-');
|
||||
|
||||
if(true_e < 0) true_e = -true_e;
|
||||
if(true_e >= 100)
|
||||
{
|
||||
kprint_out(true_e / 100 + '0');
|
||||
true_e %= 100;
|
||||
}
|
||||
kprint_out(true_e / 10 + '0');
|
||||
kprint_out(true_e % 10 + '0');
|
||||
|
||||
kprint_outn(' ', g.right_spaces);
|
||||
}
|
||||
|
||||
//---
|
||||
// Formatters for kprint
|
||||
//---
|
||||
|
||||
static void kformat_fp(KFORMAT_ARGS)
|
||||
{
|
||||
double v = va_arg(*args, double);
|
||||
digit_buffer[0] = '0';
|
||||
char *digits = digit_buffer + 1;
|
||||
int length, e;
|
||||
|
||||
int is_e = (spec | 0x20) == 'e';
|
||||
int is_f = (spec | 0x20) == 'f';
|
||||
int is_g = (spec | 0x20) == 'g';
|
||||
|
||||
/* In %e and %f, default to 6 decimals. In %g, default to 6 significant
|
||||
digits, and force to at least 1 */
|
||||
if(opt->precision < 0) opt->precision = 6;
|
||||
if(opt->precision == 0 && is_g) opt->precision = 1;
|
||||
|
||||
if(special_notation(v, (spec & 0x20) == 0)) return;
|
||||
|
||||
/* fabs(v) = int(digits) * 10^e */
|
||||
grisu2(v, digits, &length, &e);
|
||||
digits[length] = 0;
|
||||
|
||||
/* In %f and %e, .precision is the number of decimal places; in %g, it
|
||||
is the number of significant digits. Determine the number of decimal
|
||||
places for the rounding (which is one more than the final number if
|
||||
a carry creates a new significant digit on the left) */
|
||||
int round_place = opt->precision;
|
||||
if(is_e) round_place -= (length + e - 1);
|
||||
if(is_g) round_place -= (length + e);
|
||||
|
||||
/* Round off to the specified number of decimal places. digits and
|
||||
length may extend one place left because of carries */
|
||||
round_str(&digits, &length, e, round_place);
|
||||
|
||||
kprint_geometry_t g = {
|
||||
.sign = (v < 0) ? '-' : '+',
|
||||
.prefix = 0,
|
||||
.style = KPRINT_NUMERIC,
|
||||
};
|
||||
|
||||
if(is_f) return direct_notation(opt, g, digits, length, e);
|
||||
if(is_e) return exponent_notation(opt, g, digits, length, e,spec=='E');
|
||||
|
||||
int true_e = length + e - 1;
|
||||
int extreme = (true_e < -4) || (true_e >= opt->precision);
|
||||
|
||||
/* %g is left. Remove decimal zeros at the end of the digit string,
|
||||
starting from the last-shown digit. Keep all figits before the
|
||||
point, the amount of which depends on the mode */
|
||||
int removed = remove_zeros(digits, min(length, opt->precision),
|
||||
extreme ? 1 : true_e + 1);
|
||||
opt->precision -= removed;
|
||||
|
||||
if(extreme)
|
||||
{
|
||||
/* Don't print more significant digits than we have digits
|
||||
(elimination of trailing zeros) */
|
||||
opt->precision = min(opt->precision, length);
|
||||
/* There is one leading digit and this many decimal places */
|
||||
opt->precision--;
|
||||
|
||||
return exponent_notation(opt, g, digits, length, e, spec=='G');
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Number of digits before the point */
|
||||
int leading_digits = true_e + 1;
|
||||
|
||||
/* Eliminate trailing zeros after the point */
|
||||
opt->precision = min(opt->precision, length);
|
||||
/* Remove leading digits from decimal place count */
|
||||
opt->precision -= leading_digits;
|
||||
|
||||
return direct_notation(opt, g, digits, length, e);
|
||||
}
|
||||
}
|
||||
|
||||
/* kprint_enable_fp(): Enable and load floating-point formats */
|
||||
void kprint_enable_fp(void)
|
||||
{
|
||||
kprint_register('e', kformat_fp);
|
||||
kprint_register('f', kformat_fp);
|
||||
kprint_register('g', kformat_fp);
|
||||
}
|
|
@ -66,15 +66,12 @@ kprint_formatter_t kprint_formatters[26] = {
|
|||
/* kprint_register(): Register a formatter */
|
||||
void kprint_register(int spec, kprint_formatter_t kformat)
|
||||
{
|
||||
spec |= 0x20;
|
||||
int i = (spec | 0x20) - 'a';
|
||||
|
||||
/* Non-letters */
|
||||
if(i < 0 || i >= 26) return;
|
||||
if(spec < 'a' || spec > 'z') return;
|
||||
/* Size-specifying letters */
|
||||
if(spec == 'h' || spec == 'l' || spec == 'z') return;
|
||||
|
||||
kprint_formatters[i] = kformat;
|
||||
kprint_formatters[spec - 'a'] = kformat;
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -104,17 +101,23 @@ static void kprint_flush(void)
|
|||
kprint_ptr = kprint_buffer;
|
||||
}
|
||||
|
||||
/* kprint_out(): Output a single character to the buffer */
|
||||
GINLINE static void kprint_out(int byte)
|
||||
/* kprint_out(): Output a single character to the kprint buffer */
|
||||
GINLINE void kprint_out(int byte)
|
||||
{
|
||||
if(kprint_ptr >= kprint_limit) kprint_flush();
|
||||
*kprint_ptr++ = byte;
|
||||
}
|
||||
|
||||
/* kprint_outn(): Output the same character <n> times */
|
||||
GINLINE static void kprint_outn(int byte, size_t n)
|
||||
GINLINE void kprint_outn(int byte, int n)
|
||||
{
|
||||
while(n--) kprint_out(byte);
|
||||
while(n-- > 0) kprint_out(byte);
|
||||
}
|
||||
|
||||
/* kprint_outstr(): Output a string to the kprint buffer */
|
||||
GINLINE void kprint_outstr(char const *str, int n)
|
||||
{
|
||||
for(int i = 0; i < n; i++) kprint_out(str[i]);
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -125,7 +128,7 @@ GINLINE static void kprint_outn(int byte, size_t n)
|
|||
kprint_options_t kprint_opt(char const **options_ptr)
|
||||
{
|
||||
/* No options enabled by default, set the size to int */
|
||||
kprint_options_t opt = { .size = 2 };
|
||||
kprint_options_t opt = { .size = 2, .precision = -1 };
|
||||
|
||||
/* This function acts as a deterministic finite automaton */
|
||||
enum {
|
||||
|
@ -144,6 +147,7 @@ kprint_options_t kprint_opt(char const **options_ptr)
|
|||
if(c == '.')
|
||||
{
|
||||
state = precision;
|
||||
opt.precision = 0;
|
||||
continue;
|
||||
}
|
||||
else if(state == length && c >= '0' && c <= '9')
|
||||
|
@ -213,11 +217,9 @@ void kprint(char const *format, va_list *args)
|
|||
opt = kprint_opt(&format);
|
||||
spec = *format++;
|
||||
|
||||
if(spec)
|
||||
{
|
||||
int id = (spec | 0x20) - 'a';
|
||||
int id = (spec | 0x20) - 'a';
|
||||
if(id >= 0 && id < 26 && kprint_formatters[id])
|
||||
kprint_formatters[id](spec, &opt, args);
|
||||
}
|
||||
}
|
||||
|
||||
kprint_flush();
|
||||
|
@ -245,7 +247,7 @@ size_t kvsprint(char *output, size_t length, char const *format, va_list *args)
|
|||
void kformat_geometry(kprint_options_t *opt, kprint_geometry_t *g)
|
||||
{
|
||||
int integral = (g->style == KPRINT_INTEGER);
|
||||
int numerical = (g->style == KPRINT_NUMERICAL) || integral;
|
||||
int numerical = (g->style == KPRINT_NUMERIC) || integral;
|
||||
ssize_t padding;
|
||||
|
||||
/* Sign character (no discussion required for negative values) */
|
||||
|
@ -263,7 +265,7 @@ void kformat_geometry(kprint_options_t *opt, kprint_geometry_t *g)
|
|||
if(padding < 0) padding = 0;
|
||||
|
||||
/* In integral modes, precision forces zeros */
|
||||
if(integral && opt->precision)
|
||||
if(integral && opt->precision >= 0)
|
||||
{
|
||||
if(opt->alignment == '0') opt->alignment = 0;
|
||||
|
||||
|
@ -294,7 +296,6 @@ void kformat_geometry(kprint_options_t *opt, kprint_geometry_t *g)
|
|||
static int digits_10(char *str, uint64_t n)
|
||||
{
|
||||
int digits = 0;
|
||||
|
||||
while(n || !digits)
|
||||
{
|
||||
/* TODO: Use qdiv() in kprint's digits_10() */
|
||||
|
@ -421,6 +422,7 @@ static void kformat_int(KFORMAT_ARGS)
|
|||
int pure, total;
|
||||
|
||||
pure = digits_10(digits, n);
|
||||
if(opt->precision == 0 && !n) pure = 0;
|
||||
total = max(pure, opt->precision);
|
||||
g.content = total;
|
||||
|
||||
|
@ -455,6 +457,7 @@ static void kformat_uint(KFORMAT_ARGS)
|
|||
if(specl == 'o') pure = digits_8(digits, n);
|
||||
if(specl == 'x') pure = digits_16(digits, spec == 'X', n);
|
||||
|
||||
if(opt->precision == 0 && !n) pure = 0;
|
||||
total = max(pure, opt->precision);
|
||||
|
||||
/* Prefix length */
|
||||
|
@ -509,7 +512,7 @@ static void kformat_fixed(KFORMAT_ARGS)
|
|||
kprint_geometry_t g = {
|
||||
.sign = (n < 0) ? '-' : '+',
|
||||
.prefix = 0,
|
||||
.style = KPRINT_NUMERICAL,
|
||||
.style = KPRINT_NUMERIC,
|
||||
};
|
||||
if(n < 0) n = -n;
|
||||
|
||||
|
|
Loading…
Reference in a new issue