I want to get a decimal number from a json file into a float variable by using the nlohmann::json library and allways get the output 86700.2031 instead of 86700.2. First I used a numeric json type:
{
"test": 86700.2,
}
and tried this way:
using json = nlohmann::json;
std::string config_path = "C:/config/default.json";
std::ifstream config_file(config_path);
json config = json::parse(config_file);
float test = config["test"]; //Output: 86700.2031
After that I changed the json type to string and tried this with the same outcome:
float test = std::stof(config["test"].get<std::string>()); //Output: 86700.2031
Reading integers or strings works fine. How do I read a float value properly?
"float" has very low precision. Never use float but use double instead. (One day you will know where you can go against that advice. Before that, use double). The result will not be exactly 86700.2 but very close to it.
Weird though that your library returns a string when the value clearly isn't a string. I'd check the documentation for what function you should call. You should never have to convert data yourself like that.
Related
When exporting a yolo5-model to torchscript (using yolo5's export.py), the input image size has to be fixed using the --imgsz-argument.
How can I access this value (the expected image size) later using torch's C++-API?
I know that the information is there: the exported model is a zip-archive in disguise and, after unzipping, the size appears in extra/config.txt.
So I could theoretically use a zip-library to retrieve this information.
However, it feels hacky and might be unstable.
You can get the files in the extra/ directory in the zip archive using an overload of the torch::jit::load method:
torch::jit::ExtraFilesMap extra_files{{"config.txt", ""}};
m_module = torch::jit::load("path/to/model.torchscript", gpu_id, extra_files);
std::cout << extra_files.at("config.txt") << std::endl;
Will yield the content of config.txt, which contains JSON, i.a. the required shape of the input image.
It should be easy to troll the desired value from that string.
Note that this solution only works if the config.txt file has been added to the zip archive explicitly.
yolov5's export.py does that.
I am not a c++ expert but this code works for me. I saved two double variables on python and was able to read them in c++. I tried to memcpy straight from the string but I wasn't able to, that's why I created this sequence of char.
torch::jit::script::Module module;
torch::jit::ExtraFilesMap extra_files{{"var1", ""}, {"var2", ""}};
module = torch::jit::load("model.pt", device, extra_files);
std::string var1_str = extra_files.at("var1");
std::string var2_str = extra_files.at("var2");
double var1;
char b[] = {var1_str[0], var1_str[1], var1_str[2], var1_str[3], var1_str[4], var1_str[5], var1_str[6], var1_str[7]};
memcpy(&var1, &b, sizeof(var1));
printf("Var1 read: %.17g\n", var1);
This is the python code I used to save the variables:
traced_script_module = torch.jit.trace(model, sample)
extra_files = {'var1': np.array(var1).tobytes(), 'var2': np.array(var2).tobytes()}
traced_script_module.save('model.pt', _extra_files=extra_files)
This was based on #pasbi answer.
I am reading from a JSON file using jsoncpp. When I write back to the file, my float values are slightly off. For the sake of testing, I decided to parse the file to a Json::Value and then write that value back to the file. I would expect it to look the same, but instead the float values are different.
Example:
"Parameters":
{
"MinXValue": 0.1,
"MaxXValue": 0.15,
"MinYValue": 0.25,
"MaxYValue": 1.1,
"MinObjectSizeValue": 1
}
writes as:
"Parameters":
{
"MinXValue": 0.10000000000000001,
"MaxXValue": 0.14999999999999999,
"MinYValue": 0.25,
"MaxYValue": 1.1000000238418579,
"MinObjectSizeValue": 1
}
You may notice that 0.25 did not change, even though all of the other floats did. Any idea what's going on here?
It is actually an issue of floating point number parsing/printing implementation. Although floating point numbers can only represent some decimal numbers exactly (0.25 is one of ~2^64), it is necessary to parse a string representation to the nearest binary representation. When printing floating point, it is also necessary to print the (preferably the shortest) string representation which can be restored to the binary representation.
I admit that I had not investigate JsonCPP to see if there is a solution for this. But as I am the author of RapidJSON, I tried to see how RapidJSON performs for this:
const char json[] =
"{"
"\"MinXValue\": 0.1,"
"\"MaxXValue\": 0.15,"
"\"MinYValue\": 0.25,"
"\"MaxYValue\": 1.1,"
"\"MinObjectSizeValue\": 1"
"}";
using namespace rapidjson;
Document d;
d.Parse(json);
StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
d.Accept(writer);
std::cout << sb.GetString();
And the result:
{
"MinXValue": 0.1,
"MaxXValue": 0.15,
"MinYValue": 0.25,
"MaxYValue": 1.1,
"MinObjectSizeValue": 1
}
RapidJSON implemented both parsing and printing algorithms internally. Normal precision parsing will have maximum 3 ULP errors, but with full precision parsing flag (kParseFullPrecisionFlag) it can always parse to nearest representation. The printing part implemented Grisu2 algorithm. It does always generate an exact result, and more than 99% of time to be shortest (optimal).
Actually, using strtod() and sprintf(..., "%.17g", ...) can solve this problem too. But they are much slower in current C/C++ standard library. For example, I have done a benchmark for printing double. So in RapidJSON we implemented its own optimized header-only solutions.
This feature has alreay been supported, for those who are still looking into this problem: https://github.com/open-source-parsers/jsoncpp/commit/772f634548f0cec058a0f16a2e641d9f7b78343d
std::ofstream ofs("example.json");
Json::Value root;
// ... Build your json object....
Json::StreamWriterBuilder wbuilder;
wbuilder["indentation"] = "";
wbuilder.settings_["precision"] = 6;
std::unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter());
// Write to file.
writer->write(root, &ofs);
ofs.close();
One solution is to a make small change to the jsoncpp source file.
Replace the 17 with a 15 on the following line such that it reads (line 4135 in my copy):
std::string valueToString(double value) { return valueToString(value, false, 15); }
Basically it's reducing the max number of printed digits from 17 to 15, but if you're ok with that it seems to fix all the undesirable printing artefacts you mention. I think one could also argue you shouldn't be using json's to pass around >15 significant digits anyways (near the limit of double precision), but that's another story...
E.g. what used to print for me as:
"yo" : 1726.6969999999999,
now prints as:
"yo" : 1726.697,
I am trying to parse following JSON data (created with JSON.stringify). I am using libJSON 7. My code follows:
wstring jsonstr = _T("{\"Id\":0,\"Note\":\"\",\"Username\":\"user\",\"Password\":\"pass\",\"Enabled\":true,\"ProfileId\":\"\",\"UserAgent\":\"\",\"LastUsage\":0,\"Failures\":0,\"Abuses\":0}");
JSONNode jsondata = libjson::parse(jsonstr);
auto i = jsondata.begin();
auto num = i->as_float();
int idIsNan = _isnan(num);// Nonzero here
Now I expect that num == (double)0, however, it equals 1.#QNAN00000000000 instead. Does anyone know how to bypass this bug?
Also if I use as_int instead of as_float, it parses data correctly; so, it looks like this bug takes place for as_float method only.
I am guessing a bit here, since you don't show all the relevant code:
Your json structure is data, json seems to be the string you are parsing. So json.begin() would be the first character in that string, not the first json value. Since that first character is double quote, it cannot be parsed as a float -> you get the nonsense you see. Maybe this is what you want:
JSONNode data = libjson::parse(json);
auto i = data.begin(); //<-- data, not json!
auto num = i->as_float();
Please remember to give all the relevant code next time, that includes definition of all used variables (in this case, 'json').
PS: The json string you posted ends with an unmatched } - that might confuse the parser.
In C++, how can I convert a data of type float64 to a string without losing any of the data in float64? I need it to not only be converted to a string, but add a string to either side of the number and then sent to be written in a file.
Code:
string cycle("---NEW CYCLE ");
cycle+=//convert float64 to string and add to cycle
cycle+= "---\r\n";
writeText(cycle.c_str()); //writes string to txt file
Thanks.
The usual way of converting numbers to std::strings is to use std::ostringstream.
std::string stringify(float value)
{
std::ostringstream oss;
oss << value;
return oss.str();
}
// [...]
cycle += stringify(data);
You should use sprintf. See documentation here C++ Reference.
As an example it would be something like:
char str[30];
float flt = 2.4567F;
sprintf(str, "%.4g", flt );
Also I would use string::append to add the string. See here .
UPDATE
Updated code according to comment.
You can use sprintf to format the string.
I have a config file:
#X,Name,hostid,processid,executecommand,Max Runs, Max failure time
X,Main_XYZ_service,1,1,/opt/bada/bin,3,6,300
I parsed the above config file and stored each of the values in a vector of type string.
This is stored in vector<string> scanned:
//scanned[0]=X
//scanned[1]=Main_XYZ_service.........
long H_hostid = atoi(scanned[5].c_str());
how can I detect the type of the elements present in the vector?
If I call atoi() with a string that doesn't have a number, atoi() returns 0, but it also returns 0 if the string contains the number 0. How can I correctly assign a value to H_hostid?
In an absolute sense, you can't. If you encounter the string "0", you
can't know whether the user intended a string, an integer or a floating
point value. On the other hand, if you know what you need, you can try
to convert (say by using boost::lexical_cast), and generate an error
if it doesn't match. Alternatively, you can use regular expressions for
pattern matching, and decide what type you want as a result of what
pattern matches.
For a configuration file, I'd suggest the former. Just keep everything
as a string until you know what you need, then try the conversion (using
something reasonable for the conversion, which will report an error, and
not atoi).
Don't use atoi() - as you say, there is no way to detect errors. Use std::istringstream in C++, or strtol() in C.
long H_hostid;
std::istringstream stream(scanned[5]);
if (!(stream >> H_hostid)) {
// handle error
}
You could also use boost::lexical_cast, which does the same thing as that example, and throws an exception if the conversion fails.
If this is the data stored as a single string:
X,Main_XYZ_service,1,1,/opt/bada/bin,3,6,300
then the solution is, split this string using , as separator, and store each token in an array of size 8, then you can interpret each token based on the index as shown below:
char,string, int, int, string, int, int, int
0 1 2 3 4 5 6 7
Code would look like this:
std::string s = "X,Main_XYZ_service,1,1,/opt/bada/bin,3,6,300";
std::vector<std::string> tokens = split(s); //write the function yourself!
char c = tokens[0]; //token with index 0 is char
std::string service = tokens[1]; //token with index 1 is string
std::string path = tokens[4]; //token with index 4 is string
int int_indices[] = {2,3,5,6,7}; //rest of the index has int : total 5
for(int i = 0 ; i < 5 ; i++ )
{
try
{
int value = boost::lexical_cast<int>(tokens[int_indices[i]]);
//store value
}
catch(const bad_lexical_cast &)
{
std::cout << tokens[int_indices[i]] <<" is not an int" << std::endl;
}
}
Whenever you write a config file to be used by your application, you know in advance in what order the values will appear in that file. Otherwise, an xml or key value coding will be a better option to write a config file for general case. Personally, I would never create a config file as you have shown in your example.