ユーザーからの入力が10進数であるとしましょう。 5 .2155(小数点以下4桁)。自由に(int、double)など保存できます。
数値の小数点以下の桁数を調べるための賢い(または非常に簡単な)方法はありますか? (最後のビットをマスクすることによって、数値が偶数または奇数であることをどのように見つけるかという質問のようなものです)。
私が知っている2つの方法は、残念ながらあまり賢くはありませんが、これは私よりも環境の制限です:-)
1つ目は、数値をsprintf
して大きなバッファに"%.50f"
フォーマット文字列、末尾のゼロを取り除き、小数点以下の文字を数えます。これは、printf
ファミリ自体によって制限されます。または、浮動小数点の問題を完全に回避するために、(浮動小数点値をsprintf
するのではなく)ユーザーによる入力として文字列を使用することもできます。
2つ目は、整数部分を減算してから10を繰り返し乗算し、ゼロになるまで整数部分を再度減算することです。これは、浮動小数点数のコンピューター表現の制限によって制限されます。各段階で、正確に表現できない数値の問題が発生する可能性があります(したがって、.2155は実際には.215499999998である可能性があります)。次のようなもの(COMX-35とほぼ同等の私の頭の中を除いて、テストされていません):
count = 0
num = abs(num)
num = num - int(num)
while num != 0:
num = num * 10
count = count + 1
num = num - int(num)
取得する数値の種類がわかっている場合(たとえば、すべて小数点以下0〜4桁になる)、標準の浮動小数点の「トリック」を使用して適切に処理できます。たとえば、次の代わりに:
while num != 0:
使用する
while abs(num) >= 0.0000001:
数値がユーザー表現(文字列、OCRで編集されたgifファイルなど)から浮動小数点数に変換されると、必ずしも同じ数値を処理する必要はありません。したがって、厳密であまり役に立たない答えは「いいえ」です。
(case A)文字列表現からの数値の変換を回避できる場合、問題ははるかに簡単になり、小数点以下の桁を数えるだけで済みます。末尾のゼロの数をポイントして減算します。
それができない場合(case B)、小数点以下の最大数について仮定し、その数を文字列表現に変換して戻す必要があります。 round-to-evenメソッド を使用して、この最大数に丸めます。たとえば、ユーザーが1.09999999999999(仮想的に)として表される1.1を指定し、それを文字列に戻すと、「1.09999999999999」と推測されます。この数値をたとえば小数点以下4桁に丸めると、「1.1000」になります。これで、ケースAに戻ります。
頭のてっぺんから:
小数部分から始めます:.2155
繰り返し10を掛けて、ゼロになるまで数値の整数部分を捨てます。ステップ数は小数点以下の桁数になります。例えば:
.2155 * 10 = 2.155
.155 * 10 = 1.55
.55 * 10 = 5.5
.5 * 10 = 5.0
4ステップ= 10進数4桁
「自由に格納(int」)とはどういう意味ですか?intに格納すると、小数点以下がゼロになります。doubleはバイナリ形式で格納されるため、「10進数」との明白な関係も単純な関係もありません。なぜですか。入力を文字列として保持し、それらの小数をカウントするのに十分な長さで、最終的な数値変数の宛先に送信しますか?
このようなものもうまくいくかもしれません:
float i = 5.2154;
std::string s;
std::string t;
std::stringstream out;
out << i;
s = out.str();
t = s.substr(s.find(".")+1);
cout<<"number of decimal places: " << t.length();
戦いから数年後ですが、私は3行で独自の解決策を作成しました:
_string number = "543.014";
size_t dotFound;
stoi(number, &dotFound));
string(number).substr(dotFound).size()
_
もちろん、それが本当にフロートであるかどうかを事前にテストする必要があります(たとえば、stof(number) == stoi(number)
を使用)
目標は高速であるため、これは andrei alexandrescuの改善 の改善です。彼のバージョンはすでに素朴な方法よりも速かった(すべての桁で10で割る)。以下のバージョンは、少なくともx86-64およびARMのすべてのサイズで一定時間で高速ですが、2倍のバイナリコードを占有するため、キャッシュに適していません。
このバージョンのベンチマークと私の上のalexandrescuのバージョン Facebookの愚かさのPR 。
unsigned
ではなく、signed
で機能します。
inline uint32_t digits10(uint64_t v) {
return 1
+ (std::uint32_t)(v>=10)
+ (std::uint32_t)(v>=100)
+ (std::uint32_t)(v>=1000)
+ (std::uint32_t)(v>=10000)
+ (std::uint32_t)(v>=100000)
+ (std::uint32_t)(v>=1000000)
+ (std::uint32_t)(v>=10000000)
+ (std::uint32_t)(v>=100000000)
+ (std::uint32_t)(v>=1000000000)
+ (std::uint32_t)(v>=10000000000ull)
+ (std::uint32_t)(v>=100000000000ull)
+ (std::uint32_t)(v>=1000000000000ull)
+ (std::uint32_t)(v>=10000000000000ull)
+ (std::uint32_t)(v>=100000000000000ull)
+ (std::uint32_t)(v>=1000000000000000ull)
+ (std::uint32_t)(v>=10000000000000000ull)
+ (std::uint32_t)(v>=100000000000000000ull)
+ (std::uint32_t)(v>=1000000000000000000ull)
+ (std::uint32_t)(v>=10000000000000000000ull);
}
科学的記数法形式を使用する(丸め誤差を回避するため):
#include <stdio.h>
#include <string.h>
/* Counting the number of decimals
*
* 1. Use Scientific Notation format
* 2. Convert it to a string
* 3. Tokenize it on the exp sign, discard the base part
* 4. convert the second token back to number
*/
int main(){
int counts;
char *sign;
char str[15];
char *base;
char *exp10;
float real = 0.00001;
sprintf (str, "%E", real);
sign= ( strpbrk ( str, "+"))? "+" : "-";
base = strtok (str, sign);
exp10 = strtok (NULL, sign);
counts=atoi(exp10);
printf("[%d]\n", counts);
return 0;
}
[5]
これが完全なプログラムです
#include <iostream.h>
#include <conio.h>
#include <string.h>
#include <math.h>
char charary[10]={'1','2','3','4','5','6','7','8','9','0'};
int intary[10]={1,2,3,4,5,6,7,8,9,0};
char* intpart(double);
char* fractpart(double);
int main()
{
clrscr();
int count = 0;
double d = 0;
char intstr[10], fractstr[10];
cout<<"Enter a number";
cin>>d;
strcpy(intstr,intpart(d));
strcpy(fractstr,fractpart(d));
cout<<intstr<<'.'<<fractstr;
getche();
return(0);
}
char* intpart(double f)
{
char retstr[10];
int x,y,z,count1=0;
x=(int)f;
while(x>=1)
{
z=x%10;
for(y=0;y<10;y++)
{
if(z==intary[y])
{
chrstr[count1]=charary[y];
break;
}
}
x=x/10;
count1++;
}
for(x=0,y=strlen(chrstr)-1;y>=0;y--,x++)
retstr[x]=chrstr[y];
retstr[x]='\0';
return(retstr);
}
char* fractpart(double f)
{
int count=0,x,y;
f=f-(int)f;
while(f<=1)
{
f=f*10;
for(y=0;y<10;y++)
{
if((int)f==intary[y])
{
chrstr[count]=charary[y];
break;
}
}
f=f-(int)f;
if(f<=0.01 || count==4)
break;
if(f<0)
f=-f;
count++;
}
return(chrstr);
}
値を文字列として読み取り、小数点を検索し、その前後のテキストを整数として解析することをお勧めします。浮動小数点または丸め誤差はありません。
char* fractpart(double f)
{
int intary={1,2,3,4,5,6,7,8,9,0};
char charary={'1','2','3','4','5','6','7','8','9','0'};
int count=0,x,y;
f=f-(int)f;
while(f<=1)
{
f=f*10;
for(y=0;y<10;y++)
{
if((int)f==intary[y])
{
chrstr[count]=charary[y];
break;
}
}
f=f-(int)f;
if(f<=0.01 || count==4)
break;
if(f<0)
f=-f;
count++;
}
return(chrstr);
}