恥は/dev/nullへ by 初心者

プログラミング素人がのろのろと学んだことをつづっています♪

Vimでコードの折り畳み and インデントの可視化

Vimの設定の話を少々。

MQLのコードは基本的にVimで書いているのですが、Visual Studioのようにコードの折り畳みができたら便利だなと思いました。そこで調べてみたら、遥か昔からそういう機能が実装されているようです。

 

コードの折り畳み

折り畳みに関する設定内容を .vimrcに記述しました。

set foldmethod=indent
set foldlevel=8
set foldcolumn=2

1行目の記述はインデント単位で折り畳みを行うという意味です。
2行目については、foldlevelに5を指定すると、5番目に深いインデント以下(例:5番目、6番目、7番目・・・)が折り畳まれた状態でファイルが開きます。自分の場合、ファイルを開いた時点では折り畳まれていない方が嬉しいので、わざと 8 を指定しています。
3行目では、画面の左端の何列を折り畳み表示に使うかを指定しています。ネットを見ると3くらいを指定している例を何回か目にしましたが、インデントの深さが分かるだけなので、1や2でも問題ない気がしました。

 

折り畳みについては色々なコマンドが用意されていますが、自分が良く使いそうなものは以下の3つあたりでしょうか。

zc 折り畳み(カーソル位置)
zo 展開(カーソル位置)
zR 展開(ファイル全体)

 

インデントの可視化

ついでにインデントを可視化できると便利だなと思ったので、indentLineというプラグインを導入しました。プラグインを所定のディレクトリに置いてから、.vimrcに以下の記述をしました。

let g:indentLine_color_term = 111
let g:indentLine_color_gui = '#708090'
let g:indentLine_char = '¦'


以上の設定を終えた結果、以下のようになりました。


インデントが可視化された状態

f:id:philojuan:20200901024748p:plain

 

zcコマンドでコードの一部を折り畳んだ状態

f:id:philojuan:20200901024802p:plain

 

余談

世界中で使われているエディタだけあって、「こういうことがやりたいな」と思ってネットを調べると、十中八九その機能を誰かが既に作ってくれています。改めて、Vimって凄いなあと思います。

ちなみに、今でこそこんな風にVimを称賛しているものの、学生時代のレポートはEmacsで書いていました(汗)。その頃は、Vimの操作方法を面倒くさいと思っていたのです。

しかし、慣れてしまうと超便利です。今では日本語の文章もVimで書いています。

 

パラメーターで任意の時間軸を選択し、MTFの移動平均線を表示する

以前はたまにしかMTF(MultiTimeFrame)の移動平均線をチャートに表示していなかったので、必要になったら、その都度、移動平均線を描画するインディケーターを作っていました。

 

しかし、最近MTF移動平均線をチャートに入れてあれこれ調べることが増えました。こうなると、その都度作るのが面倒になってきたため、パラメーターで任意の時間軸(MTF)を選ぶ方式のコードを書きました。

 

f:id:philojuan:20200829031524p:plain

 

コード

//----------------------------------------------------------------------------
// FileName : MyMA_Multi.mq4
// Feature  : MTFのMAを表示するインディケーター
//----------------------------------------------------------------------------

#property copyright "Copyright 2020. PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property indicator_chart_window
#property strict

#property indicator_buffers 1

enum OnOff {OFF, ON};

input ENUM_TIMEFRAMES maTF     = PERIOD_CURRENT;  // MAの時間軸(MultiTimeFrame)
input int maPeriod             = 20;              // MAの期間
input ENUM_MA_METHOD method    = MODE_SMA;        // 計算方法(SMA, EMA等)
input ENUM_APPLIED_PRICE price = PRICE_CLOSE;     // 適用価格(デフォルトは終値)
input int maWidth              = 2;               // MAの太さ
input color maColor            = clrLightPink;    // MAの色
input OnOff repaint            = ON;              // リペイント

int chartTF = 0;             // チャートのTimeFrameを格納する変数
int BarsToRecalculate = 0;   // リペイントの対象となるローソク足の本数

// MA用バッファ
double MultiMA[];

int OnInit()
{
    // ラベル名を作成
    string label = ChangeTimeFrameExp(maTF) + "_" + IntegerToString(maPeriod)
                 + "_" + ChangeMAmodeExp(method);

    SetIndexStyle (0, DRAW_LINE, STYLE_SOLID, maWidth, maColor);
    SetIndexBuffer(0, MultiMA);
    SetIndexLabel (0, label);

    chartTF = Period();

    // MAをリペイントするーソク足の本数を計算
    //  → MAの時間軸(分) ÷ チャートの時間軸(分) = リペイント本数
    if(repaint && (maTF > chartTF))
    {
        if((maTF % chartTF) == 0)
            BarsToRecalculate = maTF / chartTF;
        else
            BarsToRecalculate = maTF / chartTF + 1;
    }

    return(0);
}

int start()
{
    int counted_bars = IndicatorCounted();

    if(!counted_bars)  // counted_barsが 0 のとき
    {
        // 計算対象から除外するローソク足の本数を計算
        if((maTF * maPeriod % chartTF) == 0)
            counted_bars = maTF * maPeriod / chartTF;
        else
            counted_bars = maTF * maPeriod / chartTF + 1;
    }
    else if(repaint) // counted_barsが 0 ではなく、かつ、repaintがONのとき
    {
        // リペイントするローソク足の本数を counted_bars から差し引く。
        counted_bars = counted_bars - BarsToRecalculate;
    }

    // limitは計算対象となるローソク足の本数
    int limit = Bars - counted_bars;

    for(int i = limit - 1; i >= 0; i--)
    {
        MultiMA[i] = EMPTY_VALUE;
        int shift_Multi = iBarShift(NULL, maTF, iTime(NULL, 0, i), false);

        MultiMA[i] = iMA(NULL, maTF, maPeriod, 0, method, price, shift_Multi);
    }

    return(0);
}

// MAの計算方法の表現を「SMA」等に変換する関数
//  → 「MODE_SMA」等は整数値。これを「SMA」等の文字列にして返す。
string ChangeMAmodeExp(ENUM_MA_METHOD original)
{
    string res = "";
    switch(original)
    {
        case 0:          // MODE_SMA
            res = "SMA";
            break;

        case 1:          // MODE_EMA
            res = "EMA";
            break;

        case 2:          // MODE_SMMA
            res = "SMMA";
            break;

        case 3:          // MODE_LWMA
            res = "LWMA";
            break;

        default:
            res = "ERROR";
            break;
    }

    return res;
}

// 時間軸の表現を「M1」等に変換する関数
//  → 「PERIOD_M5」等は整数値。これを「M5」等の文字列にして返す。
string ChangeTimeFrameExp(ENUM_TIMEFRAMES tf)
{
    string res = "";   // 戻り値を格納する変数

    // 通常は、引数で受け取った時間軸を使用
    int frame  = tf;

    // 引数が PERIOD_CURRENTの場合、チャートの時間軸を使用
    if(!tf) frame = Period();

    // 変換処理
    if(frame < 60)
        res = "M" + IntegerToString(frame);
    else if(frame < 1440)  // D1は1440分
        res = "H" + IntegerToString(frame / 60);
    else if(frame == 1440)
        res = "D1";
    else if(frame == 10080)
        res = "W1";
    else if(frame == 43200)
        res = "MN1";
    else
        res = "ERROR";

    return res;
}

 

 

3市場の現地時刻を表示するインディケーター

【追記】

初めてこの記事をアップした当時はロンドンやニューヨークがサマータイム期間かどうかをパラメーターで指定する仕様になっていましたが、その後、サマータイム期間かどうかを調べる関数を作成したので、このインディケーターにもその関数を組み込みました。

この結果、サマータイム期間かどうかを指定するパラメーターは除去しました。

【追記 ここまで】 


 

ネット上に3市場の現在時刻を表示するインディケーターがありました。

裁量トレードをやっている時に便利そうだなと思った一方で、フォント、表示色、文字サイズなどを自分好みにしたかったので、自分で作ってみました。

 

スクリーンショット

f:id:philojuan:20201102183654p:plain

 

ロンドンのサマータイム期間は10月最後の日曜日までですが、ニューヨークのサマータイム期間は11月最初の日曜日までです。このため、ニューヨークだけサマータイムという期間が10月の末に存在します。

その期間は以下のようになります(実際の画像)。

f:id:philojuan:20201103091625p:plain

 

 

コード

//------------------------------------------------------------------
//  FileName : ShowMarketTimes.mq4
//------------------------------------------------------------------
#property copyright "Copyright 2020, PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

input color mtColor = C'227, 2, 132'; // 表示色
input int fontsize  = 12;             // フォントサイズ
input int ctXdis    = 440;            // 表示位置(X軸)
input int ctYdis    = 2;              // 表示位置(Y軸)

// 時差用変数
datetime LondonOffset, NYOffset;

// 都市名
string LonLabel = "LON ";
string NYLabel  = "NY ";

// サマータイム期間
bool LondonSummerTime = false;
bool NYSummerTime     = false;

int OnInit()
{
    EventSetTimer(1);

    // ロンドンがサマータイムならtrueを代入
    LondonSummerTime = LondonIsSummerTime(TimeLocal(), 9, false);

    // NYがサマータイムならtrueを代入
    // 引数2と引数3は省略(デフォルト値が使われる)
    NYSummerTime     = NewYorkIsSummerTime(TimeLocal(), 9, false);

    // ロンドンがサマータイムの時
    if(LondonSummerTime)
    {
        LondonOffset = 28800;   // 時差を28800秒にする
        LonLabel  = "LON_Sum "; // 都市名を LON_Sum にする
    }
    // ロンドンがサマータイムではない時
    else
    {
        LondonOffset = 32400; // 時差を32400秒にする
        LonLabel  = "LON ";   // 都市名を LON にする(パラメーター変更時等を考慮)
    }

    // ニューヨークがサマータイムの時
    if(NYSummerTime)
    {
        NYOffset = 46800;     // 時差を46800秒にする
        NYLabel  = "NY_Sum "; // 都市名を NY_Sum にする
    }
    // ニューヨークがサマータイムではない時
    else
    {
        NYOffset = 50400;    // 通常の時差は50400秒
        NYLabel  = "NY ";    // 都市名を NY にする(パラメーター変更時等を考慮)
    }

    return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    return(rates_total);
}

void OnTimer()
{
    // 3市場の時刻
    datetime TokyoTime  = TimeLocal(); // 東京時刻を基準とする
    datetime LondonTime = TokyoTime - LondonOffset;
    datetime NYTime     = TokyoTime - NYOffset;

    string marketTimes = "TKY "   + IntegerToString(TimeHour(TokyoTime))  + ":" + IntegerToString(TimeMinute(TokyoTime), 2, '0')  + "   "
                         + LonLabel + IntegerToString(TimeHour(LondonTime)) + ":" + IntegerToString(TimeMinute(LondonTime), 2, '0') + "   "
                         + NYLabel  + IntegerToString(TimeHour(NYTime))     + ":" + IntegerToString(TimeMinute(NYTime), 2, '0');

    ObjectDelete(0, "ShowMarketTimes");
    MakeLabel("ShowMarketTimes", ctXdis, ctYdis, 20, 25);
    ObjectSetText("ShowMarketTimes", marketTimes, fontsize, "Arial Rounded MT Bold", mtColor);

}

void OnDeinit(const int reason)
{
    EventKillTimer();
    ObjectDelete(0, "ShowMarketTimes");
}

//--------------------------------------------------------------------------
// ラベル作成関数
//--------------------------------------------------------------------------
void MakeLabel(string objName,   // ラベルオブジェクトの名前
               int xDistance,    // ラベル配置位置の起点(X軸)
               int yDistance,    // ラベル配置位置の起点(Y軸)
               int xSize,        // ラベルのサイズ(横)
               int ySize         // ラベルのサイズ(縦)
              )
{
    ObjectCreate(objName, OBJ_LABEL, 0, 0, 0);
    ObjectSet(objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSet(objName, OBJPROP_XDISTANCE, xDistance);
    ObjectSet(objName, OBJPROP_YDISTANCE, yDistance);
    ObjectSet(objName, OBJPROP_XSIZE, xSize);
    ObjectSet(objName, OBJPROP_YSIZE, ySize);
    ObjectSet(objName, OBJPROP_SELECTABLE, false);
    ObjectSet(objName, OBJPROP_HIDDEN, true);
}

//--------------------------------------------------------------------------
// ロンドンがサマータイム期間か調べる関数
//--------------------------------------------------------------------------
// [説明]
//   引数として指定したローカル日時が英国のサマータイム期間ならtrueを返す。
//   サマータイム期間でない場合はfalseを返す。
// [引数]
//   第1引数:ローカル日時
//            【引数の例】TimeLocal()
//   第2引数:ローカル時間とUTCとの時差
//            【引数の例】日本時間は「UTC +9:00」なので、第2引数は 9
//   第3引数:サマータイムの開始日時と終了日時をエキスパートに表示する
//            【引数の例】表示するならtrue、表示しないならfalse
//--------------------------------------------------------------------------
bool LondonIsSummerTime(datetime localTime, int offset, bool printDate)
{
    datetime currentUTC = localTime - (offset * 3600);  // 1時間は3600秒
    
    string thisYear = IntegerToString(TimeYear(currentUTC));

    datetime March31 = StringToTime(thisYear + ".03.31 01:00");

    int dayOfMarch31 = TimeDayOfWeek(March31);

    int diffFromSun = 0;

    if(dayOfMarch31) diffFromSun = dayOfMarch31 * 86400;

    datetime MarchLastSun = March31 - diffFromSun;

    // チェック用
    if(printDate) printf("ロンドンサマータイム開始日時(UTC時刻): " + TimeToStr(MarchLastSun));
    
    // エラーチェック用コード
    if(TimeDayOfWeek(MarchLastSun)) Alert("ERROR: MarchLastSun is not Sunday!");

    datetime Oct31 = StringToTime(thisYear + ".10.31 01:00");

    int dayOfOct31 = TimeDayOfWeek(Oct31);

    if(dayOfOct31) diffFromSun = dayOfOct31 * 86400;
    else diffFromSun = 0; // 3月の計算でこの変数を使っているので上書きが必要

    datetime OctLastSun = Oct31 - diffFromSun;

    // チェック用
    if(printDate) printf("ロンドンサマータイム終了日時(UTC時刻): " + TimeToStr(OctLastSun));

    // エラーチェック用コード
    if(TimeDayOfWeek(OctLastSun)) Alert("ERROR: OctLastSun is not Sunday!");

    bool res = false;

    if(currentUTC >= MarchLastSun && currentUTC < OctLastSun){
        res = true;
    }

    return res;
}

//--------------------------------------------------------------------------
// NYがサマータイム期間か調べる関数
//--------------------------------------------------------------------------
// [説明]
//   引数として指定したローカル日時が、NYのサマータイム期間ならtrueを返す。
//   サマータイム期間でない場合はfalseを返す。
// [引数]
//   第1引数:ローカル日時
//            【引数の例】TimeLocal()
//   第2引数:ローカル時間とUTCとの時差
//            【引数の例】日本時間は「UTC +9:00」なので、第2引数は 9
//   第3引数:サマータイムの開始日時と終了日時をエキスパートに表示する
//            【引数の例】表示するならtrue、表示しないならfalse
//--------------------------------------------------------------------------
bool NewYorkIsSummerTime(datetime localTime, int offset, bool printDate)
{
    datetime currentUTC = localTime - (offset * 3600);  // 1時間は3600秒
    
    string thisYear = IntegerToString(TimeYear(currentUTC));

    datetime MarchFirst = StringToTime(thisYear + ".03.01 07:00");

    int dayOfMarchFirst = TimeDayOfWeek(MarchFirst);

    int diffFromSun = 0;
    
    if(dayOfMarchFirst) diffFromSun = (14 - dayOfMarchFirst) * 86400;
    else diffFromSun = 7 * 86400;

    datetime MarchSecondSun = MarchFirst + diffFromSun;

    // チェック用
    if(printDate) printf("NYサマータイム開始日時(UTC時刻): " + TimeToStr(MarchSecondSun));
    
    // エラーチェック用コード
    if(TimeDayOfWeek(MarchSecondSun)) Alert("ERROR: MarchSecondSun is not Sunday!");

    datetime NovFirst = StringToTime(thisYear + ".11.01 06:00");

    int dayOfNovFirst = TimeDayOfWeek(NovFirst);

    diffFromSun = 0; // 3月第2日曜日の処理にこの変数を使っているのでリセット
    if(dayOfNovFirst) diffFromSun = (7 - dayOfNovFirst) * 86400;

    datetime NovFirstSun = NovFirst + diffFromSun;

    // チェック用
    if(printDate) printf("NYサマータイム終了日時(UTC時刻): " + TimeToStr(NovFirstSun));

    // エラーチェック用コード
    if(TimeDayOfWeek(NovFirstSun)) Alert("ERROR: NovFirstSun is not Sunday!");

    bool res = false;

    if(currentUTC >= MarchSecondSun && currentUTC < NovFirstSun){
        res = true;
    }

    return res;
}

 

ArrayResizeで要素数を増やした場合、配列に格納した値に影響は生じるか? (ついでに、--i の失敗談)

タイトル通りの疑問が浮かんだので実験してみました。

必要に応じて配列の要素数を増やそうと思っているのですが、ArrayResizeで配列の要素数増加させた場合、それ以前に存在した要素の値に影響があるのか調べました。

 

実験に使用したスクリプト

#property copyright "Copyright 2020. PHILOJUAN"
#property version   "1.00"
#property strict

void OnStart()
{
    int hoge[];
    ArrayResize(hoge, 1);
    hoge[0] = 999;

    for(int i = 2; i < 11; i++){
        ArrayResize(hoge, i);
        hoge[i - 1] = i - 1;
    }
    
    for(int j = 0; j <= 9; j++){
        printf("hoge[%d]: %d", j, hoge[j]);
    }
}

 

MT4のエキスパートに出力されたもの

 hoge[9]: 9
 hoge[8]: 8
 hoge[7]: 7
 hoge[6]: 6
 hoge[5]: 5
 hoge[4]: 4
 hoge[3]: 3
 hoge[2]: 2
 hoge[1]: 1
 hoge[0]: 999

 

上記の結果を見る限り、素数を増加させる前に格納した値には影響が無いと分かったので、安心してArrayResizeを使いたいと思います^^。

 

 失敗したこと

 hoge[i - 1] = i - 1;

の行で、「i - 1」の代わりに

 --i

 を使ってしまい、ループが終了しなくなるというミスをしました(汗)。

ここで i の値を1つ減らしてしまうと、for文の「i++」と相まって、 i の値は「1減って、1増えて」を繰り返すので、いつまで経ってもfor文が終わらないことになります。

 

備考

 i - 1

という式は、i の値を変化させません。しかし、

 --i

は、以下の代入式と同じなので i の値を変化させてしまいます。

i = i - 1

 

キリ番に水平線を描画してみる

タイトル通りのコードです。ラウンドナンバーになっている価格に水平線を描画します。MQLの勉強も兼ねて書いてみました。

チャート・リーディングをする時にどのラウンドナンバーが意識されているか調べることが主な使用目的です。

 

使い方

基本的にはデフォルトのまま実行して良いと思います。

デフォルトでは「0にする桁数」(SelectedDigits)が「2」なので、ダブルオーに水平線を描画します。

もし下3桁が0(つまり、000)の価格に水平線を描画したい場合、「0にする桁数」を「3」にします。

 

コード(スクリプト版)

//--------------------------------------------------------------
// FileName : DrawHLineRoundNum_Script.mq4
//
// Feature  : This script draws horizontal lines at level of 
//            round numbers.
//--------------------------------------------------------------
#property copyright "Copyright 2020. PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict
#property script_show_inputs

// ***************** パラメーター *****************
input int SelectedDigits = 2; // 0 にする桁数(下2桁を00にする場合は 2)
input int Step           = 1; // ステップ数(1 ~ 9の整数)
extern int BarsToSearch  = 0; // 検索対象とする本数(0 はチャート全体)
input color LineColor    = clrDarkTurquoise; // 水平線の色
input ENUM_LINE_STYLE LineStyle  = STYLE_SOLID; // 水平線のスタイル

//<簡単な説明>
// SelectedDigits
// 0 にする桁数。例えば、下2桁を00にする場合は 2 を入力。
//
// Step
// 下2桁を0(つまり、00)にしているとして実例を示すと・・・
// Step = 1 なら、価格の下3桁が 100 → 200 → 300 という幅で水平線を描画
// Step = 2 なら、価格の下3桁が 100 → 300 → 500 という値幅で水平線を描画
// Step = 3 なら、価格の下3桁が 100 → 400 → 700 という値幅で水平線を描画
//
// BarsToSearch
// キリ番表示範囲の上限と下限となる高値・安値を調べる時、1本前から
// 何本前までを検索対象とするかを指定する。
// 0を指定した場合は、(現在足を除く)チャート上の全てのローソク足
// が対象となる。


int OnStart()
{
    // 不正なパラメーターが指定された場合の処理 1
    if((Digits < SelectedDigits) || (SelectedDigits < 1)){
        Alert("Error : Read the message displayed on Expert!");
        printf("「0にする桁数」が不正です。この通貨ペアの場合、指定できるのは 1 ~ %d です。", Digits);
        return(INIT_PARAMETERS_INCORRECT);
    }

    // 不正なパラメーターが指定された場合の処理 2
    if(Step < 1 || Step > 9){
        Alert("Error : Read the message displayed on Expert!");
        printf("「ステップ数」が不正です。指定できるのは 1 ~ 9 です。");
        return(INIT_PARAMETERS_INCORRECT);
    }

    // 不正なパラメーターが指定された場合の処理 3
    if(BarsToSearch < 0 || (BarsToSearch > Bars - 1)){
        Alert("Error : Read the message displayed on Expert!");
        printf("「検索対象とする本数」が不正です。このチャートの場合、指定できるのは 0 ~ %d です。", Bars - 1);
        return(INIT_PARAMETERS_INCORRECT);
    }

    if(!BarsToSearch)
        BarsToSearch = Bars - 1;

    double LowestPrice  = Low[iLowest(  NULL, 0, MODE_LOW, BarsToSearch, 1)];
    double HighestPrice = High[iHighest(NULL, 0, MODE_HIGH, BarsToSearch, 1)];

    double multiplier  = MathPow(10, Digits - SelectedDigits); // 乗数
    int RoundPriceLow  = (int)(LowestPrice * multiplier);  // LowestPriceに乗数を掛けて、小数点以下を切捨て
    int RoundPriceHigh = (int)(HighestPrice * multiplier); // HighestPriceに乗数を掛けて、小数点以下を切捨て

    for(int i = RoundPriceLow, j = 0; i <= RoundPriceHigh; i += Step, j++){
        
        double DrawPrice = i / multiplier; // 小数点の位置を元に戻した値 (multiplierはdouble型)
        
        string hLineName = "uhoRoundNumLine" + IntegerToString(j);  // オブジェクト名を生成
        // [備考] 他のオブジェクト名と被らないように、変わった名称の方が無難

        if(ObjectFind(0, hLineName) < 0){     // 同じ名前のオブジェクトが無いかチェック
            ObjectCreate(    0, hLineName, OBJ_HLINE, 0, 0, DrawPrice);
            ObjectSetInteger(0, hLineName, OBJPROP_COLOR, LineColor);
            ObjectSetInteger(0, hLineName, OBJPROP_STYLE, LineStyle);
            ObjectSetInteger(0, hLineName, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, hLineName, OBJPROP_BACK, false);
            ObjectSetInteger(0, hLineName, OBJPROP_SELECTABLE, true);
            ObjectSetInteger(0, hLineName, OBJPROP_SELECTED, false);
        }
    }

    return(0); 
}

 

上述のスクリプトで描画した水平線を削除するコード

//--------------------------------------------------------------
//
// FileName : DeleteHLineRoundNum_Script.mq4
//
//--------------------------------------------------------------
#property copyright "Copyright 2020, PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict

void OnStart()
{
    int numObj = ObjectsTotal();

    for(int k = 0; k < numObj; k++){
        string hLineName = "uhoRoundNumLine" + IntegerToString(k);

        if(ObjectFind(0, hLineName) >= 0)
            ObjectDelete(0, hLineName);
    }
}

 

 

浮動小数点数を受けるtype変換指定子 on Visual Studio 2019

 [この記事はまだ書きかけです・・・]

 

以下の実験コードを書いてみました。

#include <stdio.h>

int main(void)
{
    double uho;
    scanf("%f", &uho);
    printf("%f", uho);
    
    return 0;
}

このコードをビルドしようとしたら、以下のメッセージが表示されました。

warning C4477 : 'scanf' : 書式文字列 '%f' には、型 'float *' の引数が必要ですが、可変個引数 1 は型 'double *' です
書式文字列に '%lf' を使用することをお勧めします

 

 

どうやら、scanf()においてdouble型を受け取る場合、「%lf」とする必要があるみたいです。一方、printf()においては「%f」のままでも問題無さそうです。

 

リンク先の文書を読むと、printf()において、「f」はdouble型に、「lf」はlong double型に使えるようですが、上述したコンパイラのメッセージによると、scanf()で「%f」はfloat型の引数を想定するようです。

 

この違いは何なのでしょうか???(scanf()に関する文書を調べる必要がありそうです。)

 

整数を受けるtype変換指定子 on Visual Studio 2019

type変換指定子に関する文書

Visual Studio 2019のtype変換指定子(ページ中段)
https://docs.microsoft.com/ja-jp/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=vs-2019

日本語の説明文がぎこちない時は「英語で読む」をONにした方がいいです^^;。

 

★%dと%iの違いに関する情報
https://www.it-swarm.dev/ja/c++/%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E3%81%95%E3%82%8C%E3%81%9Fio%E9%96%A2%E6%95%B0%E3%81%AE%E5%A4%89%E6%8F%9B%E6%8C%87%E5%AE%9A%E5%AD%90%EF%BC%85i%E3%81%A8%EF%BC%85d%E3%81%AE%E9%81%95%E3%81%84%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B%EF%BC%88-printf-scanf%EF%BC%89/968640762/

 

各データ型が取る範囲に関する文書

整数値

https://docs.microsoft.com/ja-jp/cpp/c-language/cpp-integer-limits?view=vs-2019

 

浮動小数点数

https://docs.microsoft.com/ja-jp/cpp/c-language/limits-on-floating-point-constants?view=vs-2019

 

型に応じたtype変換指定子と実験結果

実験結果を書く前に・・・、unsigned int と unsigned long int について

unsigned int       %u
unsigned long int  %lu

と某書籍に書かれていたのですが・・・、Visual Studio 2019では unsigned int と unsigned long intの最大値が同じなので、%u と %lu は実質的に同じです。

 


 

 <結果1> short int として受け取る %hd

32767 を %hd で受けると: 32767
short intは 2 バイト(-32768 ~ 32767)

 

 <結果2> %hd(short int)の範囲をオーバーした場合

32770 を %hd で受けると: -32766
short intは 2 バイト(-32768 ~ 32767)

 

<結果3> long int として受け取る %ld

2147483647 を %ld で受けると: 2147483647
long intは 4 バイト(-2147483648 ~ 2147483647)

 

<結果4> %ld(long int)の範囲をオーバーした場合

2147483650 を %ld で受けると: -2147483646
long intは 4 バイト(-2147483648 ~ 2147483647)

 

<結果5> unsigned int として受け取る %u

4294967295 を %u で受けると: 4294967295
unsigned intは 4 バイト(最大値 4294967295)

 

<結果6> %u(unsigned int)の範囲をオーバーした場合

4294967296 を %u で受けると: 0
unsigned intは 4 バイト(最大値 4294967295)

[備考] %uではなく%llu(または %I64u)を使いなさいとメッセージが出た。

 

<結果7> long long int として受け取る %lld

4294967296 を %lld で受けると: 4294967296
long long intは 8 バイト(-9223372036854775808 ~ 9223372036854775807)

 

<結果8> %lld(long long int)の範囲をオーバーした場合

9223372036854775810 を %lld で受けると: -9223372036854775806
long long intは 8 バイト(-9223372036854775808 ~ 9223372036854775807)

 

<結果9> unsigned long long int として受け取る %llu

18446744073709551615 を %llu で受けると: 18446744073709551615
unsigned long long intは 8 バイト(最大値 18446744073709551615)

[備考] 最大値は1844京以上。

 

<結果10> %llu(unsigned long long int)の範囲をオーバーした場合

unsigned long long intの最大値よりも大きい値を指定してビルド
しようとしたらビルドできず・・・。

 

上記結果の元となったコード

<コード1>

#include <stdio.h>
#include <limits.h>

int main(void)
{
    printf("32767 を %%hd で受けると: %hd\n", 32767);
    printf("short intは %d バイト(%d ~ %d)\n", sizeof(short int), SHRT_MIN, SHRT_MAX);
    return 0;
}

 

<コード2>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("32770 を %%hd で受けると: %hd\n", 32770); printf("short intは %d バイト(%d ~ %d)\n", sizeof(short int), SHRT_MIN, SHRT_MAX); return 0; }

 

<コード3>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("2147483647 を %%ld で受けると: %ld\n", 2147483647); printf("long intは %d バイト(%d ~ %d)\n", sizeof(long int), LONG_MIN, LONG_MAX); return 0; }

 

<コード4>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("2147483650 を %%ld で受けると: %ld\n", 2147483650); printf("long intは %d バイト(%d ~ %d)\n", sizeof(long int), LONG_MIN, LONG_MAX); return 0; }

 

<コード5>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("4294967295 を %%u で受けると: %u\n", 4294967295); printf("unsigned intは %d バイト(最大値 %u)\n", sizeof(unsigned int), UINT_MAX); return 0; }

 

<コード6>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("4294967296 を %%u で受けると: %u\n", 4294967296); printf("unsigned intは %d バイト(最大値 %u)", sizeof(unsigned int), UINT_MAX); return 0; }

 

<コード7>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("4294967296 を %%lld で受けると: %lld\n", 4294967296); printf("long long intは %d バイト(%lld ~ %lld)", sizeof(long long int), LLONG_MIN, LLONG_MAX); return 0; }

 

<コード8>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("9223372036854775810 を %%lld で受けると: %lld\n", 9223372036854775810); printf("long long intは %d バイト(%lld ~ %lld)", sizeof(long long int), LLONG_MIN, LLONG_MAX); return 0; }

 

<コード9>

#include <stdio.h> 
#include <limits.h>

int main(void) { printf("18446744073709551615 を %%llu で受けると: %llu\n", 18446744073709551615); printf("unsigned long long intは %d バイト(最大値 %llu)", sizeof(unsigned long long int), ULLONG_MAX); return 0; }

 

<コード10>

/* このコードはビルドできなかった */
#include <stdio.h>
#include <limits.h>
int main(void) { printf("18446744073709551620 を %%llu で受けると: %llu\n", 18446744073709551620); printf("unsigned long long intは %d バイト(最大値 %llu)", sizeof(unsigned long long int), ULLONG_MAX); return 0; }