【今日の学び】浮動小数点の比較と関数テンプレート
double や float を == で比較するのは良くないこととされている。
内部的に2進数として扱われているため、丸め誤差が発生するからだ。
また、概念的には、整数型も浮動小数点型も比較関数でやりたいことは一緒なので、テンプレートを使いたくなる。
#include <iostream>
template <typename T>
bool comp(T a, T b){
return a == b;
}
void printBool(bool x){
if(x){
std::cout << "true" << std::endl;
}else{
std::cout << "false" << std::endl;
}
}
int main(){
char c = 123;
int x = 12345;
long y = 1234567L;
printBool(comp(c, c));
printBool(comp(x, x));
printBool(comp(y, y));
}
これは整数型(char, short, int, long など)についてはうまく動作する。
しかし。浮動小数点も含めようとすると難しい。
浮動小数点の場合は、
#include <cfloat>
bool comp(double a, double b){
return std::abs(a - b) < DBL_EPSILON;
}
を使いたい。関数を直接呼ぶ場合は、引数のデータ型でうまく呼び分けてくれる。
しかし、std 内で定義されているアルゴリズムのように、比較関数を引数で受け取るようにするのが難しかった。
#include <iostream>
#include <cfloat>
template <typename T>
bool comp(T a, T b){
return a == b;
}
bool comp(double a, double b){
return std::abs(a - b) < DBL_EPSILON;
}
void printBool(bool x){
if(x){
std::cout << "true" << std::endl;
}else{
std::cout << "false" << std::endl;
}
}
using CompF = bool (*)(double, double);
void printBool2(double a, double b, CompF f = comp){
if(f(a, b)){
std::cout << "true" << std::endl;
}else{
std::cout << "false" << std::endl;
}
}
int main(){
char c = 123;
int x = 12345;
long y = 1234567L;
printBool(comp(c, c));
printBool(comp(x, x));
printBool(comp(y, y));
printBool(comp(1.0, 1.0));
printBool2(1.0, 1.0, comp);
printBool2(1.0, 1.0);
}
ナップサック問題
ようやく動的計画法が少しわかってきた。
-- given-- goods:(体積,価値)のリスト-- capacity: 体積の総和の上限値-- unknown-- 体積の総和の上限値を超えない範囲で、-- totalValue: (体積,価値)を組合せたときの価値の総和の最大値-- selected : 価値の総和が最大になる(体積,価値)の組合せ
import Data.Arraygoods = [(3,1), (4,2), (5,3)]
-- 容量制限 -> 価値の総和の最大値calc :: Integer -> Integercalc capacity = totalValue ! capacity -- 配列 totalValue の capacity 番目の要素が戻り値となるwheretotalValue = array (0, capacity) [(volume, maximum $ map(\(w, v) -> if volume < w then 0 else totalValue ! (volume - w) + v)goods) | volume <- [0..capacity]]
-- capacity: 容量上限値。 volume の総和 <= capacity の制約のもと、-- totalValue :value の総和の最大値 を求めるmain :: IO ()main = do capacity <- readLnprint $ calc capacity
Haskell の ! がなんの意味かわからなくて困った。配列の要素を参照する演算子らしい。
Prelude> import Data.Array
Prelude Data.Array> let a = array (1,5)[(i,i^2) | i <-[1..5]]
Prelude Data.Array> print a
array (1,5) [(1,1),(2,4),(3,9),(4,16),(5,25)]
Prelude Data.Array> print $ a ! 3
9
Haskell における配列の使い方 が為になった。
上記のコードだと、価値の最大値はわかるものの、その最大値となる品物の組合せがわからない。組合せを得るには、表を作ったあと、最大値となるつながりを逆にたどる必要がある。
病みつきになる「動的計画法」、その深淵に迫る (1/4) - ITmedia エンタープライズ
逆にたどるには、マスを埋めるときにどのマスから来たのかを記録しておく必要がある。従って、マスは(価値の和, もとのマス)というタプルになるはず。
Data.Array は連想配列らしい。
第19回 配列でデータ・アクセスの効率を上げる | 日経クロステック(xTECH)
このように,Ixのクラスのインスタンスにはタプルも含まれます。このため,タプルを渡すことで,複数の値が組になった多次元の配列を表現できます。
これを使えばよいはず。使い方を勉強しよう。
STP8M のピン配置
aitendo で購入した300円のステッピングモータ。データシートがないので、ピン配置をテスターで調べてみた。2個のステッピングモータなので8ピンのコネクタになっている。コードの色が違うので、どっちのモータに繋がっているのかはすぐわかる。
4本の端子がA相とB相に別れるわけだが、テスタによると隣り合う2ピンが導通していた。これだけわかれば、回転させられるはず。
ちなみに。コネクタは1.25mmピッチだが秋月で購入しておいた 1.27mm なピンヘッダを挿すことができた。ピンヘッダを使ってテスタのプローブを当てることができた。
clang でカバレッジ
foo.cpp
12 #define BAR(x) *1$
13 template <typename T> void foo(T x) {$
14 for (unsigned I = 0; I < 10; ++I) { BAR(I); }$
15 }$
16 int main() {$
17 foo<int>(0);$
18 foo<float>(0);$
19 return 0;$
20 }$
build.sh
1 #!/bin/bash$
2 $
3 clang -fprofile-arcs -ftest-coverage -c foo.cpp$
4 clang --coverage foo.o -o foo$
5 ./foo$
6 llvm-cov gcov -f -b foo.gcda$
7 lcov -d . -b . --gcov-tool ./llvm-gcov.sh -c -o foo.info$
8 genhtml foo.info -o html$
llvm-gcov.sh
*1:x) || (x
【Maixduino】スピーカーと動画
Maixduino上にDACとアンプが載っているため、スピーカーをつなぐだけで音が鳴らせる。
Maixduino · kzono/RISC-V Wiki · GitHub
標準で搭載されているコネクタが 1.25mm ピッチなのが困ったところ。これにあうハウジングがなかなか見当たらないし、自分でピンを圧着するのもかなり面倒。
しかし、今回は aitendo でちょうどいいコネクタ付きケーブルを見つけることができた。また、aitendo でミニスピーカーも安価に購入した。
実際に鳴らしてみると、思ったよりいい音で鳴った。
調子に乗って Video の再生デモを動かそうとしたが、音はでるものの何故か画像が表示されない。いまのところ原因不明。
試しに録画するデモを動かしたところ、動画ファイルが作成された。それを動画再生デモで再生すると、無事に動画が再生された!
今日はここまで。
【Maixduino】micropython でサーボモータを回す
本当はC/C++で VSCode + PlatformIO + kendryteSDK な環境でサーボモータを回したかったのだが。
ビルドには成功するものの、何故か書き込んでも動かない。
正確には、platformio:upload は、ファームウェア書き込み用の python コードがエラーになって書き込めない。そこで、GUI な kflush.py を使って書き込んだのだが、動かない。
firmware.bin を書き込むのだが、書き込む際にその先頭アドレスを指定できる。書き込むべきアドレスを間違えている気がしている。0x00000 がデフォルト値。
割り込みベクタテーブル(特にリセット時の割り込みベクタ)がそこにあるようなきがする。
Maixpy のイメージファイルを書き込む場合は、そのアドレスに書き込んで正しく動いている。
kendryteSDK で作った firmware.bin の書き込み先アドレスは何番地が正しいのか?
仕方がないので、maixpy をMaixduino に書き込み、micropython でサーボモータを PWM で制御するコードを書いたところ、無事に動いた!
Maixduino · kzono/RISC-V Wiki · GitHub
ampy を使うと、書いたスクリプトをダウンロードして実行できるようだ。
```
ampy --port=/dev/ttyUSB0 --baud 115200 run maixduino_pwm.py
```
[Maixduino でサーボモータを回してみた](https://youtu.be/M-RLBJP2qis)