質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.02%
GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

数値を基数変換して表示したい

eipi-1-0
eipi-1-0

総合スコア8

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1回答

0グッド

0クリップ

244閲覧

投稿2022/11/15 08:19

前提

c++で数値を基数変換し格納、表示するクラスを作っています

実現したいこと

コンストラクタ
数値を入力し、n進数の各桁を配列に格納したい

発生している問題・エラーメッセージ

2進数で1が連続した値 (2^n - 1) が出力され、正しく基数変換できない

問題があると思われるコードを抜粋、修正したものです
入力された数値を2進数で逆順に出力する(ことを期待したい)コードです

c++

1#include <cstdint> 2#include <iostream> 3int main(){ 4 uint64_t num; 5 std::cin >> num; 6 for(uint8_t i = 0; i < 64; i++){ 7 std::cout << (num != (num >>= 1) << 1); // 問題箇所 8 } 9 std::cout << std::endl; 10 return 0; 11}

該当のソースコード

c++

1// main.cpp 2#include <cstdint> 3#include <iostream> 4#include "base.hpp" 5int main(){ 6 uint64_t a; 7 std::cin >> a; 8 std::cout << Base(2, a) << std::endl; 9 return 0; 10}

c++

1// base.hpp 2#ifndef base_hpp 3#define base_hpp 4#include <cstdint> 5#include <iostream> 6class Base{ 7 char* number; 8 uint64_t base; 9 uint64_t* num; 10 uint64_t digit; 11 void add(uint64_t* base_a, const uint64_t* base_b); 12public: 13 Base(const char* number, uint64_t num, uint64_t digit = 64); 14 Base(uint64_t base, uint64_t num, uint64_t digit = 64); 15 friend std::ostream& operator<<(std::ostream& os, Base base_a); 16}; 17#endif

c++

1// base.cpp 2#include "base.hpp" 3void Base::add(uint64_t* base_a, const uint64_t* base_b){ 4 uint8_t carry = 0; 5 for(uint64_t i = 0; i < digit; i++){ 6 uint64_t temp = base_a[i] + base_b[i] + carry; 7 carry = temp < base_a[i] + carry || base <= temp; 8 base_a[i] = carry? temp - base: temp; 9 } 10 return; 11} 12Base::Base(const char* number, uint64_t num, uint64_t digit): 13base(0), num(new uint64_t[digit]{}), digit(digit){ 14 while(number[base]) base++; 15 this->number = new char[base]; 16 for(uint64_t i = 0; i < base; i++){ 17 this->number[i] = number[i]; 18 } 19 uint64_t* twice = new uint64_t[digit]{1}; 20 if(num != (num >>= 1) << 1) add(this->num, twice); // 問題箇所 21 for(uint64_t i = 1; i < digit; i++){ 22 add(twice, twice); 23 if(num != (num >>= 1) << 1) add(this->num, twice); // 問題箇所 24 } 25 return; 26} 27Base::Base(uint64_t base, uint64_t num, uint64_t digit): 28number(new char[base]{}), base(base), num(new uint64_t[digit]{}), digit(digit){ 29 const char number[] = 30 "0123456789" 31 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 32 "abcdefghijklmnopqrstuvwxyz"; 33 for(uint64_t i = 0; i < base && i < 62; i++){ 34 this->number[i] = number[i]; 35 } 36 uint64_t* twice = new uint64_t[digit]{1}; 37 if(num != (num >>= 1) << 1) add(this->num, twice); // 問題箇所 38 for(uint64_t i = 1; i < digit; i++){ 39 add(twice, twice); 40 if(num != (num >>= 1) << 1) add(this->num, twice); // 問題箇所 41 } 42 return; 43} 44std::ostream& operator<<(std::ostream& os, Base base_a){ 45 uint64_t digit = base_a.digit; 46 uint64_t i = 0; 47 for(; i < digit; i++) if(base_a.num[digit - i - 1]) break; 48 if(i == digit) os << base_a.number[0]; 49 else for(; i < digit; i++) os << base_a.number[base_a.num[digit - i - 1]]; 50 return os; 51}

試したこと

問題箇所の式を次のように変更したところ、うまく動作しました

c++

1// uint64_t temp; 2// ... 3((temp = num) != (num >>= 1) << 1);

c++

1(num != num >> 1 << 1); 2num >>= 1;

補足情報(FW/ツールのバージョンなど)

google colaboratory で g++ を使用しています

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答1

1

ベストアンサー

(num != (num >>= 1) << 1)のような、「代入した変数を、(副作用完了点を超えない)同じ式の別の場所で参照するコード」は、C++では評価順が決まっておらず、未定義の動作となります。

素直に、numのシフトとビットごとの評価を別な式で行いましょう。

投稿2022/11/15 08:37

編集2022/11/15 08:39
maisumakun

総合スコア141550

eipi-1-0👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

eipi-1-0

2022/11/15 09:03

なるほど、式の始めの位置以外での代入の使用や、1つの式の中で複数の代入を行うことは良い書き方ではないのですね 早急なご回答ありがとうございました。大変助かりました
maisumakun

2022/11/15 09:10

> 式の始めの位置以外での代入の使用や、1つの式の中で複数の代入を行うことは良い書き方ではないのですね どちらも微妙に外しているのですが…(特に、この式には「1つの式の中で複数の代入」は含まれていません)
eipi-1-0

2022/11/16 00:25

分かりにくい書き方をしてしまい申し訳ありません この式に限らず一般的な式の話です。 可読性の低下や未定義動作となる可能性があるためという意図です
maisumakun

2022/11/16 00:31

> この式に限らず一般的な式の話です。 そうであっても、「式の始めの位置以外での代入の使用や、1つの式の中で複数の代入」そのものは未定義の動作にはなりません。あくまで、「同じ変数」を複数回使った場合の問題です。
maisumakun

2022/11/16 00:32

たとえば、a=b=50;のような書き方はよく使われます。
eipi-1-0

2022/12/17 14:44

返信遅れて申し訳ありません なかなか難しいですが理解することができました 丁寧に回答して頂きありがとうございました。

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.02%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。