diff --git a/README.md b/README.md index eb43775..f9abb38 100644 --- a/README.md +++ b/README.md @@ -514,6 +514,8 @@ Licensed under either of Apache License, Version href="LICENSE-BOOST">BOOST license. +
+ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this repository by you, as defined in the Apache-2.0 license, diff --git a/script/amalgamate.py b/script/amalgamate.py index 3565922..78e9961 100644 --- a/script/amalgamate.py +++ b/script/amalgamate.py @@ -1,100 +1,122 @@ # text parts -processed_files = { } +processed_files = {} # authors -for filename in ['AUTHORS', 'CONTRIBUTORS']: - with open(filename, encoding='utf8') as f: - text = '' - for line in f: - if filename == 'AUTHORS': - text += '// fast_float by ' + line - if filename == 'CONTRIBUTORS': - text += '// with contributions from ' + line - processed_files[filename] = text + '//\n//\n' +for filename in ["AUTHORS", "CONTRIBUTORS"]: + with open(filename, encoding="utf8") as f: + text = "" + for line in f: + if filename == "AUTHORS": + text += "// fast_float by " + line + if filename == "CONTRIBUTORS": + text += "// with contributions from " + line + processed_files[filename] = text + "//\n//\n" # licenses -for filename in ['LICENSE-MIT', 'LICENSE-APACHE', 'LICENSE-BOOST']: - lines = [] - with open(filename, encoding='utf8') as f: - lines = f.readlines() +for filename in ["LICENSE-MIT", "LICENSE-APACHE", "LICENSE-BOOST"]: + lines = [] + with open(filename, encoding="utf8") as f: + lines = f.readlines() - # Retrieve subset required for inclusion in source - if filename == 'LICENSE-APACHE': - lines = [ - ' Copyright 2021 The fast_float authors\n', - *lines[179:-1] - ] + # Retrieve subset required for inclusion in source + if filename == "LICENSE-APACHE": + lines = [" Copyright 2021 The fast_float authors\n", *lines[179:-1]] - text = '' - for line in lines: - line = line.strip() - if len(line): - line = ' ' + line - text += '//' + line + '\n' - processed_files[filename] = text + text = "" + for line in lines: + line = line.strip() + if len(line): + line = " " + line + text += "//" + line + "\n" + processed_files[filename] = text # code -for filename in [ 'constexpr_feature_detect.h', 'float_common.h', 'fast_float.h', 'ascii_number.h', - 'fast_table.h', 'decimal_to_binary.h', 'bigint.h', 'digit_comparison.h', 'parse_number.h']: - with open('include/fast_float/' + filename, encoding='utf8') as f: - text = '' - for line in f: - if line.startswith('#include "'): continue - text += line - processed_files[filename] = '\n' + text +for filename in [ + "constexpr_feature_detect.h", + "float_common.h", + "fast_float.h", + "ascii_number.h", + "fast_table.h", + "decimal_to_binary.h", + "bigint.h", + "digit_comparison.h", + "parse_number.h", +]: + with open("include/fast_float/" + filename, encoding="utf8") as f: + text = "" + for line in f: + if line.startswith('#include "'): + continue + text += line + processed_files[filename] = "\n" + text # command line import argparse -parser = argparse.ArgumentParser(description='Amalgamate fast_float.') -parser.add_argument('--license', default='TRIPLE', choices=['DUAL', 'TRIPLE', 'MIT', 'APACHE', 'BOOST'], help='choose license') -parser.add_argument('--output', default='', help='output file (stdout if none') +parser = argparse.ArgumentParser(description="Amalgamate fast_float.") +parser.add_argument( + "--license", + default="TRIPLE", + choices=["DUAL", "TRIPLE", "MIT", "APACHE", "BOOST"], + help="choose license", +) +parser.add_argument("--output", default="", help="output file (stdout if none") args = parser.parse_args() + def license_content(license_arg): - result = [] - if license_arg == 'TRIPLE': - result += [ - '// Licensed under the Apache License, Version 2.0, or the\n', - '// MIT License or the Boost License. This file may not be copied,\n', - '// modified, or distributed except according to those terms.\n', - '//\n' - ] - if license_arg == 'DUAL': - result += [ - '// Licensed under the Apache License, Version 2.0, or the\n', - '// MIT License at your option. This file may not be copied,\n', - '// modified, or distributed except according to those terms.\n', - '//\n' - ] + result = [] + if license_arg == "TRIPLE": + result += [ + "// Licensed under the Apache License, Version 2.0, or the\n", + "// MIT License or the Boost License. This file may not be copied,\n", + "// modified, or distributed except according to those terms.\n", + "//\n", + ] + if license_arg == "DUAL": + result += [ + "// Licensed under the Apache License, Version 2.0, or the\n", + "// MIT License at your option. This file may not be copied,\n", + "// modified, or distributed except according to those terms.\n", + "//\n", + ] + + if license_arg in ("DUAL", "TRIPLE", "MIT"): + result.append("// MIT License Notice\n//\n") + result.append(processed_files["LICENSE-MIT"]) + result.append("//\n") + if license_arg in ("DUAL", "TRIPLE", "APACHE"): + result.append("// Apache License (Version 2.0) Notice\n//\n") + result.append(processed_files["LICENSE-APACHE"]) + result.append("//\n") + if license_arg in ("TRIPLE", "BOOST"): + result.append("// BOOST License Notice\n//\n") + result.append(processed_files["LICENSE-BOOST"]) + result.append("//\n") - if license_arg in ('DUAL', 'TRIPLE', 'MIT'): - result.append('// MIT License Notice\n//\n') - result.append(processed_files['LICENSE-MIT']) - result.append('//\n') - if license_arg in ('DUAL', 'TRIPLE', 'APACHE'): - result.append('// Apache License (Version 2.0) Notice\n//\n') - result.append(processed_files['LICENSE-APACHE']) - result.append('//\n') - if license_arg in ('TRIPLE', 'BOOST'): - result.append('// BOOST License Notice\n//\n') - result.append(processed_files['LICENSE-BOOST']) - result.append('//\n') + return result - return result -text = ''.join([ - processed_files['AUTHORS'], processed_files['CONTRIBUTORS'], - *license_content(args.license), - processed_files['constexpr_feature_detect.h'], - processed_files['float_common.h'], processed_files['fast_float.h'], - processed_files['ascii_number.h'], processed_files['fast_table.h'], - processed_files['decimal_to_binary.h'], processed_files['bigint.h'], - processed_files['digit_comparison.h'], processed_files['parse_number.h']]) +text = "".join( + [ + processed_files["AUTHORS"], + processed_files["CONTRIBUTORS"], + *license_content(args.license), + processed_files["constexpr_feature_detect.h"], + processed_files["float_common.h"], + processed_files["fast_float.h"], + processed_files["ascii_number.h"], + processed_files["fast_table.h"], + processed_files["decimal_to_binary.h"], + processed_files["bigint.h"], + processed_files["digit_comparison.h"], + processed_files["parse_number.h"], + ] +) if args.output: - with open(args.output, 'wt', encoding='utf8') as f: - f.write(text) + with open(args.output, "wt", encoding="utf8") as f: + f.write(text) else: - print(text) + print(text) diff --git a/script/analysis.py b/script/analysis.py index 8dcbcd5..08f5c49 100644 --- a/script/analysis.py +++ b/script/analysis.py @@ -1,36 +1,38 @@ +import sys from math import floor + def log2(x): - """returns ceil(log2(x)))""" - y = 0 - while((1<= 2**127 - K = 2**127 - if(not(c * K * d<=( K + 1) * t)): - print(q) - top = floor(t/(c * d - t)) - sys.exit(-1) + t = 2 ** b + c = t // d + 1 + assert c < 2 ** 128 + assert c >= 2 ** 127 + K = 2 ** 127 + if not (c * K * d <= (K + 1) * t): + print(q) + top = floor(t / (c * d - t)) + sys.exit(-1) -for q in range(18, 344+1): - d = 5**q - b = 64 + 2*log2(d) - t = 2**b - c = t//d + 1 - assert c > 2**(64 +log2(d)) - K = 2**64 - if(not(c * K * d<=( K + 1) * t)): - print(q) - top = floor(t/(c * d - t)) - sys.exit(-1) +for q in range(18, 344 + 1): + d = 5 ** q + b = 64 + 2 * log2(d) + t = 2 ** b + c = t // d + 1 + assert c > 2 ** (64 + log2(d)) + K = 2 ** 64 + if not (c * K * d <= (K + 1) * t): + print(q) + top = floor(t / (c * d - t)) + sys.exit(-1) -print("all good") \ No newline at end of file +print("all good") diff --git a/script/mushtak_lemire.py b/script/mushtak_lemire.py index 5b98fda..46c8c64 100644 --- a/script/mushtak_lemire.py +++ b/script/mushtak_lemire.py @@ -9,25 +9,25 @@ # Appendix B of Number parsing at a gigabyte per second. # Software: Practice and Experience 2021;51(8):1700–1727. for q in range(-342, -27): - power5 = 5**-q + power5 = 5 ** -q z = 0 while (1 << z) < power5: z += 1 b = 2 * z + 2 * 64 - c = 2**b // power5 + 1 + c = 2 ** b // power5 + 1 while c >= (1 << 128): c //= 2 all_tqs.append(c) for q in range(-27, 0): - power5 = 5**-q + power5 = 5 ** -q z = 0 while (1 << z) < power5: z += 1 b = z + 127 - c = 2**b // power5 + 1 + c = 2 ** b // power5 + 1 all_tqs.append(c) for q in range(0, 308 + 1): - power5 = 5**q + power5 = 5 ** q while power5 < (1 << 127): power5 *= 2 while power5 >= (1 << 128): @@ -44,6 +44,7 @@ def continued_fraction(numer, denom): numer, denom = denom, rem return cf + # Given a continued fraction [a0; a1, a2, ..., an], returns # all the convergents of that continued fraction # as pairs of the form (numer, denom), where numer/denom is @@ -58,17 +59,22 @@ def convergents(cf): p_n = a_n * p_n_minus_1 + p_n_minus_2 q_n = a_n * q_n_minus_1 + q_n_minus_2 convergents.append((p_n, q_n)) - p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = p_n_minus_1, q_n_minus_1, p_n, q_n + p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = ( + p_n_minus_1, + q_n_minus_1, + p_n, + q_n, + ) return convergents # Enumerate through all the convergents of T[q] / 2^137 with denominators < 2^64 found_solution = False for j, tq in enumerate(all_tqs): - for _, w in convergents(continued_fraction(tq, 2**137)): - if w >= 2**64: + for _, w in convergents(continued_fraction(tq, 2 ** 137)): + if w >= 2 ** 64: break - if (tq*w) % 2**137 > 2**137 - 2**64: + if (tq * w) % 2 ** 137 > 2 ** 137 - 2 ** 64: print(f"SOLUTION: q={j-342} T[q]={tq} w={w}") found_solution = True if not found_solution: diff --git a/script/release.py b/script/release.py index 064a10e..4bcf6c8 100755 --- a/script/release.py +++ b/script/release.py @@ -8,129 +8,176 @@ import io import os import fileinput + if sys.version_info < (3, 0): sys.stdout.write("Sorry, requires Python 3.x or better\n") sys.exit(1) + def colored(r, g, b, text): - return "\033[38;2;{};{};{}m{} \033[38;2;255;255;255m".format(r, g, b, text) + return f"\033[38;2;{r};{g};{b}m{text} \033[38;2;255;255;255m" + def extractnumbers(s): - return tuple(map(int,re.findall(r"(\d+)\.(\d+)\.(\d+)",str(s))[0])) + return tuple(map(int, re.findall(r"(\d+)\.(\d+)\.(\d+)", str(s))[0])) + def toversionstring(major, minor, rev): - return str(major)+"."+str(minor)+"."+str(rev) + return f"{major}.{minor}.{rev}" + -def topaddedversionstring(major, minor, rev): - return str(major)+str(minor).zfill(3)+str(rev).zfill(3) print("Calling git rev-parse --abbrev-ref HEAD") -pipe = subprocess.Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) +pipe = subprocess.Popen( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, +) branchresult = pipe.communicate()[0].decode().strip() -if(branchresult != "main"): - print(colored(255, 0, 0, "We recommend that you release on main, you are on '"+branchresult+"'")) +if branchresult != "main": + print( + colored( + 255, + 0, + 0, + f"We recommend that you release on main, you are on '{branchresult}'", + ) + ) ret = subprocess.call(["git", "remote", "update"]) -if(ret != 0): +if ret != 0: sys.exit(ret) print("Calling git log HEAD.. --oneline") -pipe = subprocess.Popen(["git", "log", "HEAD..", "--oneline"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) +pipe = subprocess.Popen( + ["git", "log", "HEAD..", "--oneline"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, +) uptodateresult = pipe.communicate()[0].decode().strip() -if(len(uptodateresult) != 0): +if len(uptodateresult) != 0: print(uptodateresult) sys.exit(-1) -pipe = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) +pipe = subprocess.Popen( + ["git", "rev-parse", "--show-toplevel"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, +) maindir = pipe.communicate()[0].decode().strip() scriptlocation = os.path.dirname(os.path.abspath(__file__)) -print("repository: "+maindir) +print(f"repository: {maindir}") -pipe = subprocess.Popen(["git", "describe", "--abbrev=0", "--tags"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) +pipe = subprocess.Popen( + ["git", "describe", "--abbrev=0", "--tags"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, +) versionresult = pipe.communicate()[0].decode().strip() -print("last version: "+versionresult ) +print(f"last version: {versionresult}") try: - currentv = extractnumbers(versionresult) + currentv = extractnumbers(versionresult) except: - currentv = [0,0,0] -if(len(sys.argv) != 2): - nextv = (currentv[0],currentv[1], currentv[2]+1) - print ("please specify version number, e.g. "+toversionstring(*nextv)) + currentv = [0, 0, 0] +if len(sys.argv) != 2: + nextv = (currentv[0], currentv[1], currentv[2] + 1) + print(f"please specify version number, e.g. {toversionstring(*nextv)}") sys.exit(-1) try: newversion = extractnumbers(sys.argv[1]) print(newversion) except: - print("can't parse version number "+sys.argv[1]) + print(f"can't parse version number {sys.argv[1]}") sys.exit(-1) print("checking that new version is valid") -if(newversion[0] != currentv[0]): - assert newversion[0] == currentv[0] + 1 +if newversion[0] != currentv[0]: + assert newversion[0] == currentv[0] + 1 assert newversion[1] == 0 assert newversion[2] == 0 -elif (newversion[1] != currentv[1]): - assert newversion[1] == currentv[1] + 1 +elif newversion[1] != currentv[1]: + assert newversion[1] == currentv[1] + 1 assert newversion[2] == 0 -else : - assert newversion[2] == currentv[2] + 1 - -atleastminor= (currentv[0] != newversion[0]) or (currentv[1] != newversion[1]) - +else: + assert newversion[2] == currentv[2] + 1 +atleastminor = (currentv[0] != newversion[0]) or (currentv[1] != newversion[1]) newmajorversionstring = str(newversion[0]) -mewminorversionstring = str(newversion[1]) -newrevversionstring = str(newversion[2]) -newversionstring = str(newversion[0]) + "." + str(newversion[1]) + "." + str(newversion[2]) -cmakefile = maindir + os.sep + "CMakeLists.txt" - - -for line in fileinput.input(cmakefile, inplace=1, backup='.bak'): - line = re.sub(r'project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)','project(fast_float VERSION '+newmajorversionstring+'.'+mewminorversionstring+'.'+newrevversionstring+" LANGUAGES CXX)", line.rstrip()) +newminorversionstring = str(newversion[1]) +newpatchversionstring = str(newversion[2]) +newversionstring = f"{newversion[0]}.{newversion[1]}.{newversion[2]}" +cmakefile = f"{maindir}{os.sep}CMakeLists.txt" + +for line in fileinput.input(cmakefile, inplace=1, backup=".bak"): + line = re.sub( + r"project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)", + f"project(fast_float VERSION {newversionstring} LANGUAGES CXX)", + line.rstrip(), + ) print(line) -print("modified "+cmakefile+", a backup was made") - - - -versionfilerel = os.sep + "include" + os.sep + "fast_float" + os.sep + "float_common.h" -versionfile = maindir + versionfilerel - -for line in fileinput.input(versionfile, inplace=1, backup='.bak'): - line = re.sub(r'#define FASTFLOAT_VERSION_MAJOR \d+','#define FASTFLOAT_VERSION_MAJOR '+newmajorversionstring, line.rstrip()) - line = re.sub(r'#define FASTFLOAT_VERSION_MINOR \d+','#define FASTFLOAT_VERSION_MAJOR '+mewminorversionstring, line.rstrip()) - line = re.sub(r'#define FASTFLOAT_VERSION_PATCH \d+','#define FASTFLOAT_VERSION_MAJOR '+newrevversionstring, line.rstrip()) +print(f"modified {cmakefile}, a backup was made") + +versionfilerel = f"{os.sep}include{os.sep}fast_float{os.sep}float_common.h" +versionfile = f"{maindir}{versionfilerel}" + +for line in fileinput.input(versionfile, inplace=1, backup=".bak"): + line = re.sub( + r"#define FASTFLOAT_VERSION_MAJOR \d+", + f"#define FASTFLOAT_VERSION_MAJOR {newmajorversionstring}", + line.rstrip(), + ) + line = re.sub( + r"#define FASTFLOAT_VERSION_MINOR \d+", + f"#define FASTFLOAT_VERSION_MINOR {newminorversionstring}", + line.rstrip(), + ) + line = re.sub( + r"#define FASTFLOAT_VERSION_PATCH \d+", + f"#define FASTFLOAT_VERSION_PATCH {newpatchversionstring}", + line.rstrip(), + ) print(line) -print(versionfile + " modified") - - -readmefile = maindir + os.sep + "README.md" - - -for line in fileinput.input(readmefile, inplace=1, backup='.bak'): - line = re.sub(r'https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h','https://github.com/fastfloat/fast_float/releases/download/v'+newmajorversionstring+'.'+mewminorversionstring+'.'+newrevversionstring+'/fast_float.h', line.rstrip()) +print(f"{versionfile} modified") + +readmefile = f"{maindir}{os.sep}README.md" + +for line in fileinput.input(readmefile, inplace=1, backup=".bak"): + line = re.sub( + r"https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h", + f"https://github.com/fastfloat/fast_float/releases/download/v{newversionstring}/fast_float.h", + line.rstrip(), + ) + line = re.sub( + r"GIT_TAG tags/v(\d+\.\d+\.\d+)", + f"GIT_TAG tags/v{newversionstring}", + line.rstrip(), + ) + line = re.sub( + r"GIT_TAG v(\d+\.\d+\.\d+)\)", f"GIT_TAG v{newversionstring})", line.rstrip() + ) print(line) -print("modified "+readmefile+", a backup was made") - +print(f"modified {readmefile}, a backup was made") print("running amalgamate.py") -with open(maindir+ os.sep + 'fast_float.h', "w") as outfile: - cp = subprocess.run(["python3", maindir+ os.sep + "script/amalgamate.py"], stdout=outfile) +with open(f"{maindir}{os.sep}fast_float.h", "w") as outfile: + cp = subprocess.run( + [f"python3", f"{maindir}{os.sep}script{os.sep}amalgamate.py"], stdout=outfile + ) -if(cp.returncode != 0): +if cp.returncode != 0: print("Failed to run amalgamate") else: print("amalgamate.py ran successfully") - print("You should upload "+ maindir+ os.sep + 'fast_float.h') - -print("Please run the tests before issuing a release. \n") -print("to issue release, enter \n git commit -a && git push && git tag -a v"+toversionstring(*newversion)+" -m \"version "+toversionstring(*newversion)+"\" && git push --tags \n") - - + print(f"You should upload {maindir}{os.sep}fast_float.h") +print("Please run the tests before issuing a release.\n") +print( + f'to issue release, enter\n git commit -a && git push && git tag -a v{toversionstring(*newversion)} -m "version {toversionstring(*newversion)}" && git push --tags\n' +) diff --git a/script/table_generation.py b/script/table_generation.py index 24fec7c..223240f 100644 --- a/script/table_generation.py +++ b/script/table_generation.py @@ -1,14 +1,15 @@ def format(number): - upper = number // (1<<64) - lower = number % (1<<64) - print(""+hex(upper)+","+hex(lower)+",") + upper = number // (1 << 64) + lower = number % (1 << 64) + print("" + hex(upper) + "," + hex(lower) + ",") -for q in range(-342,0): + +for q in range(-342, 0): power5 = 5 ** -q z = 0 - while( (1<= -27): + if q >= -27: b = z + 127 c = 2 ** b // power5 + 1 format(c) @@ -16,16 +17,16 @@ def format(number): b = 2 * z + 2 * 64 c = 2 ** b // power5 + 1 # truncate - while(c >= (1<<128)): - c //= 2 + while c >= (1 << 128): + c //= 2 format(c) -for q in range(0,308+1): +for q in range(0, 308 + 1): power5 = 5 ** q # move the most significant bit in position - while(power5 < (1<<127)): + while power5 < (1 << 127): power5 *= 2 # *truncate* - while(power5 >= (1<<128)): + while power5 >= (1 << 128): power5 //= 2 format(power5)